diff options
Diffstat (limited to 'sources/shiboken2')
149 files changed, 13601 insertions, 4863 deletions
diff --git a/sources/shiboken2/ApiExtractor/CMakeLists.txt b/sources/shiboken2/ApiExtractor/CMakeLists.txt index b67a352f0..b4fd1cb99 100644 --- a/sources/shiboken2/ApiExtractor/CMakeLists.txt +++ b/sources/shiboken2/ApiExtractor/CMakeLists.txt @@ -37,6 +37,7 @@ abstractmetabuilder.cpp abstractmetalang.cpp fileout.cpp graph.cpp +messages.cpp reporthandler.cpp typeparser.cpp typesystem.cpp diff --git a/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp b/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp index 84c116708..f6724e61d 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp +++ b/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp @@ -27,6 +27,7 @@ ****************************************************************************/ #include "abstractmetabuilder_p.h" +#include "messages.h" #include "reporthandler.h" #include "typedatabase.h" @@ -159,38 +160,18 @@ AbstractMetaEnumList AbstractMetaBuilder::globalEnums() const return d->m_globalEnums; } -static QString msgNoFunctionForModification(const QString &signature, - const QString &originalSignature, - const QString &className, - const QStringList &possibleSignatures, - const AbstractMetaFunctionList &allFunctions) -{ - QString result; - QTextStream str(&result); - str << "signature '" << signature << '\''; - if (!originalSignature.isEmpty() && originalSignature != signature) - str << " (specified as '" << originalSignature << "')"; - str << " for function modification in '" - << className << "' not found."; - if (possibleSignatures.isEmpty()) { - str << " No candidates were found. Member functions: "; - for (int f = 0, size = allFunctions.size(); f < size; ++f) { - if (f) - str << ", "; - str << allFunctions.at(f)->minimalSignature(); - } - } else { - str << " Possible candidates: " << possibleSignatures.join(QLatin1String(", ")); - } - return result; +AbstractMetaEnum *AbstractMetaBuilder::findEnum(const TypeEntry *typeEntry) const +{ + if (typeEntry && typeEntry->isFlags()) + typeEntry = static_cast<const FlagsTypeEntry*>(typeEntry)->originator(); + return d->m_enums.value(typeEntry); } void AbstractMetaBuilderPrivate::checkFunctionModifications() { - TypeDatabase *types = TypeDatabase::instance(); - const SingleTypeEntryHash entryHash = types->entries(); + const auto &entries = TypeDatabase::instance()->entries(); - for (SingleTypeEntryHash::const_iterator it = entryHash.cbegin(), end = entryHash.cend(); it != end; ++it) { + for (auto it = entries.cbegin(), end = entries.cend(); it != end; ++it) { const TypeEntry *entry = it.value(); if (!entry) continue; @@ -241,7 +222,7 @@ void AbstractMetaBuilderPrivate::checkFunctionModifications() } } -AbstractMetaClass *AbstractMetaBuilderPrivate::argumentToClass(ArgumentModelItem argument) +AbstractMetaClass *AbstractMetaBuilderPrivate::argumentToClass(const ArgumentModelItem &argument) { AbstractMetaClass* returned = 0; AbstractMetaType *type = translateType(argument->type()); @@ -256,7 +237,7 @@ AbstractMetaClass *AbstractMetaBuilderPrivate::argumentToClass(ArgumentModelItem /** * Checks the argument of a hash function and flags the type if it is a complex type */ -void AbstractMetaBuilderPrivate::registerHashFunction(FunctionModelItem function_item) +void AbstractMetaBuilderPrivate::registerHashFunction(const FunctionModelItem &function_item) { ArgumentList arguments = function_item->arguments(); if (arguments.size() == 1) { @@ -269,12 +250,12 @@ void AbstractMetaBuilderPrivate::registerHashFunction(FunctionModelItem function * Check if a class has a debug stream operator that can be used as toString */ -void AbstractMetaBuilderPrivate::registerToStringCapability(FunctionModelItem function_item) +void AbstractMetaBuilderPrivate::registerToStringCapability(const FunctionModelItem &function_item) { ArgumentList arguments = function_item->arguments(); if (arguments.size() == 2) { if (arguments.at(0)->type().toString() == QLatin1String("QDebug")) { - ArgumentModelItem arg = arguments.at(1); + const ArgumentModelItem &arg = arguments.at(1); if (AbstractMetaClass *cls = argumentToClass(arg)) { if (arg->type().indirections() < 2) cls->setToStringCapability(true); @@ -283,7 +264,7 @@ void AbstractMetaBuilderPrivate::registerToStringCapability(FunctionModelItem fu } } -void AbstractMetaBuilderPrivate::traverseOperatorFunction(FunctionModelItem item) +void AbstractMetaBuilderPrivate::traverseOperatorFunction(const FunctionModelItem &item) { if (item->accessPolicy() != CodeModel::Public) return; @@ -348,7 +329,7 @@ void AbstractMetaBuilderPrivate::traverseOperatorFunction(FunctionModelItem item setupFunctionDefaults(metaFunction, baseoperandClass); baseoperandClass->addFunction(metaFunction); Q_ASSERT(!metaFunction->wasPrivate()); - } else if (metaFunction) { + } else { delete metaFunction; } @@ -356,7 +337,7 @@ void AbstractMetaBuilderPrivate::traverseOperatorFunction(FunctionModelItem item } } -void AbstractMetaBuilderPrivate::traverseStreamOperator(FunctionModelItem item) +void AbstractMetaBuilderPrivate::traverseStreamOperator(const FunctionModelItem &item) { ArgumentList arguments = item->arguments(); if (arguments.size() == 2 && item->accessPolicy() == CodeModel::Public) { @@ -404,7 +385,7 @@ void AbstractMetaBuilderPrivate::traverseStreamOperator(FunctionModelItem item) funcClass->typeEntry()->addExtraInclude(streamClass->typeEntry()->include()); m_currentClass = oldCurrentClass; - } else if (streamFunction) { + } else { delete streamFunction; } @@ -422,7 +403,7 @@ void AbstractMetaBuilderPrivate::fixQObjectForScope(const FileModelItem &dom, TypeEntry* entry = types->findType(qualifiedName); if (entry) { if (isQObject(dom, qualifiedName) && entry->isComplex()) - ((ComplexTypeEntry*) entry)->setQObject(true); + static_cast<ComplexTypeEntry *>(entry)->setQObject(true); } } @@ -475,19 +456,18 @@ void AbstractMetaBuilderPrivate::traverseDom(const FileModelItem &dom) const ClassList &typeValues = dom->classes(); ReportHandler::setProgressReference(typeValues); for (const ClassModelItem &item : typeValues) { - ReportHandler::progress(QLatin1String("Generating class model...")); - AbstractMetaClass *cls = traverseClass(dom, item); - if (!cls) - continue; - - addAbstractMetaClass(cls); + ReportHandler::progress(QStringLiteral("Generating class model (%1)...") + .arg(typeValues.size())); + if (AbstractMetaClass *cls = traverseClass(dom, item)) + addAbstractMetaClass(cls); } // We need to know all global enums const EnumList &enums = dom->enums(); ReportHandler::setProgressReference(enums); for (const EnumModelItem &item : enums) { - ReportHandler::progress(QLatin1String("Generating enum model...")); + ReportHandler::progress(QStringLiteral("Generating enum model (%1)...") + .arg(enums.size())); AbstractMetaEnum *metaEnum = traverseEnum(item, 0, QSet<QString>()); if (metaEnum) { if (metaEnum->typeEntry()->generateCode()) @@ -495,10 +475,11 @@ void AbstractMetaBuilderPrivate::traverseDom(const FileModelItem &dom) } } - const QSet<NamespaceModelItem> &namespaceTypeValues = dom->uniqueNamespaces(); + const auto &namespaceTypeValues = dom->namespaces(); ReportHandler::setProgressReference(namespaceTypeValues); for (const NamespaceModelItem &item : namespaceTypeValues) { - ReportHandler::progress(QLatin1String("Generating namespace model...")); + ReportHandler::progress(QStringLiteral("Generating namespace model (%1)...") + .arg(namespaceTypeValues.size())); AbstractMetaClass *metaClass = traverseNamespace(dom, item); if (metaClass) m_metaClasses << metaClass; @@ -509,11 +490,14 @@ void AbstractMetaBuilderPrivate::traverseDom(const FileModelItem &dom) const TypeDefList typeDefs = dom->typeDefs(); ReportHandler::setProgressReference(typeDefs); for (const TypeDefModelItem &typeDef : typeDefs) { - ReportHandler::progress(QLatin1String("Resolving typedefs...")); - AbstractMetaClass* cls = traverseTypeDef(dom, typeDef); - addAbstractMetaClass(cls); + ReportHandler::progress(QStringLiteral("Resolving typedefs (%1)...") + .arg(typeDefs.size())); + if (AbstractMetaClass *cls = traverseTypeDef(dom, typeDef)) + addAbstractMetaClass(cls); } + traverseTypesystemTypedefs(); + for (const ClassModelItem &item : typeValues) traverseClassMembers(item); @@ -580,13 +564,12 @@ void AbstractMetaBuilderPrivate::traverseDom(const FileModelItem &dom) if (cls->isAbstract() && !cls->isInterface()) cls->typeEntry()->setLookupName(cls->typeEntry()->targetLangName() + QLatin1String("$ConcreteWrapper")); } - const TypeEntryHash allEntries = types->allEntries(); - ReportHandler::progress(QLatin1String("Detecting inconsistencies in typesystem...")); - for (TypeEntryHash::const_iterator it = allEntries.cbegin(), end = allEntries.cend(); it != end; ++it) { - for (TypeEntry *entry : it.value()) { - if (entry->isPrimitive()) - continue; - + const auto &allEntries = types->entries(); + ReportHandler::progress(QStringLiteral("Detecting inconsistencies in typesystem (%1)...") + .arg(allEntries.size())); + for (auto it = allEntries.cbegin(), end = allEntries.cend(); it != end; ++it) { + TypeEntry *entry = it.value(); + if (!entry->isPrimitive()) { if ((entry->isValue() || entry->isObject()) && !entry->isString() && !entry->isChar() @@ -616,20 +599,13 @@ void AbstractMetaBuilderPrivate::traverseDom(const FileModelItem &dom) } } } else if (entry->isEnum() && (entry->generateCode() & TypeEntry::GenerateTargetLang)) { - const QString name = ((EnumTypeEntry*) entry)->targetLangQualifier(); + const QString name = static_cast<const EnumTypeEntry *>(entry)->targetLangQualifier(); AbstractMetaClass *cls = AbstractMetaClass::findClass(m_metaClasses, name); - bool enumFound = false; - if (cls) { - enumFound = cls->findEnum(entry->targetLangName()); - } else { // Global enum - for (AbstractMetaEnum *metaEnum : qAsConst(m_enums)) { - if (metaEnum->typeEntry() == entry) { - enumFound = true; - break; - } - } - } + const bool enumFound = cls + ? cls->findEnum(entry->targetLangName()) != nullptr + : m_enums.contains(entry); + if (!enumFound) { entry->setCodeGeneration(TypeEntry::GenerateNothing); qCWarning(lcShiboken).noquote().nospace() @@ -727,6 +703,15 @@ void AbstractMetaBuilderPrivate::traverseDom(const FileModelItem &dom) std::puts(""); } +static bool metaEnumLessThan(const AbstractMetaEnum *e1, const AbstractMetaEnum *e2) +{ return e1->fullName() < e2->fullName(); } + +static bool metaClassLessThan(const AbstractMetaClass *c1, const AbstractMetaClass *c2) +{ return c1->fullName() < c2->fullName(); } + +static bool metaFunctionLessThan(const AbstractMetaFunction *f1, const AbstractMetaFunction *f2) +{ return f1->name() < f2->name(); } + bool AbstractMetaBuilder::build(const QByteArrayList &arguments, LanguageLevel level, unsigned clangFlags) @@ -737,6 +722,14 @@ bool AbstractMetaBuilder::build(const QByteArrayList &arguments, if (ReportHandler::isDebug(ReportHandler::MediumDebug)) qCDebug(lcShiboken) << dom.data(); d->traverseDom(dom); + + // Ensure that indexes are in alphabetical order, roughly + std::sort(d->m_globalEnums.begin(), d->m_globalEnums.end(), metaEnumLessThan); + std::sort(d->m_metaClasses.begin(), d->m_metaClasses.end(), metaClassLessThan); + std::sort(d->m_templates.begin(), d->m_templates.end(), metaClassLessThan); + std::sort(d->m_smartPointers.begin(), d->m_smartPointers.end(), metaClassLessThan); + std::sort(d->m_globalFunctions.begin(), d->m_globalFunctions.end(), metaFunctionLessThan); + return true; } @@ -749,9 +742,6 @@ void AbstractMetaBuilder::setLogDirectory(const QString& logDir) void AbstractMetaBuilderPrivate::addAbstractMetaClass(AbstractMetaClass *cls) { - if (!cls) - return; - cls->setOriginalAttributes(cls->attributes()); if (cls->typeEntry()->isContainer()) { m_templates << cls; @@ -827,8 +817,7 @@ AbstractMetaClass *AbstractMetaBuilderPrivate::traverseNamespace(const FileModel } // Traverse namespaces recursively - const QSet<NamespaceModelItem> &innerNamespaces = namespaceItem->uniqueNamespaces(); - for (const NamespaceModelItem &ni : innerNamespaces) { + for (const NamespaceModelItem &ni : namespaceItem->namespaces()) { AbstractMetaClass* mjc = traverseNamespace(dom, ni); if (mjc) { metaClass->addInnerClass(mjc); @@ -848,7 +837,7 @@ AbstractMetaClass *AbstractMetaBuilderPrivate::traverseNamespace(const FileModel return metaClass; } -AbstractMetaEnum *AbstractMetaBuilderPrivate::traverseEnum(EnumModelItem enumItem, +AbstractMetaEnum *AbstractMetaBuilderPrivate::traverseEnum(const EnumModelItem &enumItem, AbstractMetaClass *enclosing, const QSet<QString> &enumsDeclarations) { @@ -857,7 +846,7 @@ AbstractMetaEnum *AbstractMetaBuilderPrivate::traverseEnum(EnumModelItem enumIte TypeEntry* typeEntry = 0; if (enumItem->accessPolicy() == CodeModel::Private) { QStringList names = enumItem->qualifiedName(); - QString enumName = names.constLast(); + const QString &enumName = names.constLast(); QString nspace; if (names.size() > 1) nspace = QStringList(names.mid(0, names.size() - 1)).join(colonColon()); @@ -892,15 +881,23 @@ AbstractMetaEnum *AbstractMetaBuilderPrivate::traverseEnum(EnumModelItem enumIte return 0; } - if ((!typeEntry || !typeEntry->isEnum())) { - if (!m_currentClass || - (m_currentClass->typeEntry()->codeGeneration() & TypeEntry::GenerateTargetLang)) { - qCWarning(lcShiboken).noquote().nospace() - << QStringLiteral("enum '%1' does not have a type entry or is not an enum") - .arg(qualifiedName); + const bool rejectionWarning = !m_currentClass + || (m_currentClass->typeEntry()->codeGeneration() & TypeEntry::GenerateTargetLang); + + if (!typeEntry) { + if (rejectionWarning) + qCWarning(lcShiboken, "%s", qPrintable(msgNoEnumTypeEntry(enumItem, className))); + m_rejectedEnums.insert(qualifiedName, AbstractMetaBuilder::NotInTypeSystem); + return nullptr; + } + + if (!typeEntry->isEnum()) { + if (rejectionWarning) { + qCWarning(lcShiboken, "%s", + qPrintable(msgNoEnumTypeConflict(enumItem, className, typeEntry))); } m_rejectedEnums.insert(qualifiedName, AbstractMetaBuilder::NotInTypeSystem); - return 0; + return nullptr; } AbstractMetaEnum *metaEnum = new AbstractMetaEnum; @@ -946,15 +943,9 @@ AbstractMetaEnum *AbstractMetaBuilderPrivate::traverseEnum(EnumModelItem enumIte qCDebug(lcShiboken) << " - " << metaEnumValue->name() << " = " << metaEnumValue->value() << " = " << metaEnumValue->value(); } - - // Add into global register... - if (enclosing) - m_enumValues[enclosing->name() + colonColon() + metaEnumValue->name()] = metaEnumValue; - else - m_enumValues[metaEnumValue->name()] = metaEnumValue; } - m_enums << metaEnum; + m_enums.insert(typeEntry, metaEnum); if (!metaEnum->typeEntry()->include().isValid()) setInclude(metaEnum->typeEntry(), enumItem->fileName()); @@ -962,6 +953,15 @@ AbstractMetaEnum *AbstractMetaBuilderPrivate::traverseEnum(EnumModelItem enumIte metaEnum->setOriginalAttributes(metaEnum->attributes()); // Register all enum values on Type database + QString prefix; + if (enclosing) { + prefix += enclosing->typeEntry()->qualifiedCppName(); + prefix += colonColon(); + } + if (enumItem->enumKind() == EnumClass) { + prefix += enumItem->name(); + prefix += colonColon(); + } const EnumeratorList &enumerators = enumItem->enumerators(); for (const EnumeratorModelItem &e : enumerators) { QString name; @@ -969,11 +969,12 @@ AbstractMetaEnum *AbstractMetaBuilderPrivate::traverseEnum(EnumModelItem enumIte name += enclosing->name(); name += colonColon(); } - name += e->name(); EnumValueTypeEntry *enumValue = - new EnumValueTypeEntry(name, e->stringValue(), + new EnumValueTypeEntry(prefix + e->name(), e->stringValue(), enumTypeEntry, enumTypeEntry->version()); TypeDatabase::instance()->addType(enumValue); + if (e->value().isNullValue()) + enumTypeEntry->setNullValue(enumValue); } return metaEnum; @@ -1014,7 +1015,7 @@ AbstractMetaClass* AbstractMetaBuilderPrivate::traverseTypeDef(const FileModelIt AbstractMetaClass *metaClass = new AbstractMetaClass; metaClass->setTypeDef(true); metaClass->setTypeEntry(type); - metaClass->setBaseClassNames(QStringList() << typeDef->type().qualifiedName().join(colonColon())); + metaClass->setBaseClassNames(QStringList(typeDef->type().toString())); *metaClass += AbstractMetaAttributes::Public; // Set the default include file name @@ -1026,6 +1027,22 @@ AbstractMetaClass* AbstractMetaBuilderPrivate::traverseTypeDef(const FileModelIt return metaClass; } +// Add the typedef'ed classes +void AbstractMetaBuilderPrivate::traverseTypesystemTypedefs() +{ + const auto &entries = TypeDatabase::instance()->typedefEntries(); + for (auto it = entries.begin(), end = entries.end(); it != end; ++it) { + TypedefEntry *te = it.value(); + AbstractMetaClass *metaClass = new AbstractMetaClass; + metaClass->setTypeDef(true); + metaClass->setTypeEntry(te->target()); + metaClass->setBaseClassNames(QStringList(te->sourceType())); + *metaClass += AbstractMetaAttributes::Public; + fillAddedFunctions(metaClass); + addAbstractMetaClass(metaClass); + } +} + AbstractMetaClass *AbstractMetaBuilderPrivate::traverseClass(const FileModelItem &dom, const ClassModelItem &classItem) { @@ -1197,25 +1214,24 @@ void AbstractMetaBuilderPrivate::traverseNamespaceMembers(NamespaceModelItem ite traverseScopeMembers(item, metaClass); // Inner namespaces - const QSet<NamespaceModelItem> &innerNamespaces = item->uniqueNamespaces(); - for (const NamespaceModelItem &ni : innerNamespaces) + for (const NamespaceModelItem &ni : item->namespaces()) traverseNamespaceMembers(ni); m_currentClass = oldCurrentClass; } -static inline QString fieldSignatureWithType(VariableModelItem field) +static inline QString fieldSignatureWithType(const VariableModelItem &field) { return field->name() + QStringLiteral(" -> ") + field->type().toString(); } static inline QString qualifiedFieldSignatureWithType(const QString &className, - VariableModelItem field) + const VariableModelItem &field) { return className + colonColon() + fieldSignatureWithType(field); } -AbstractMetaField *AbstractMetaBuilderPrivate::traverseField(VariableModelItem field, +AbstractMetaField *AbstractMetaBuilderPrivate::traverseField(const VariableModelItem &field, const AbstractMetaClass *cls) { QString fieldName = field->name(); @@ -1272,7 +1288,7 @@ AbstractMetaField *AbstractMetaBuilderPrivate::traverseField(VariableModelItem f return metaField; } -void AbstractMetaBuilderPrivate::traverseFields(ScopeModelItem scope_item, +void AbstractMetaBuilderPrivate::traverseFields(const ScopeModelItem &scope_item, AbstractMetaClass *metaClass) { const VariableList &variables = scope_item->variables(); @@ -1327,14 +1343,8 @@ void AbstractMetaBuilderPrivate::fixReturnTypeOfConversionOperator(AbstractMetaF static bool _compareAbstractMetaTypes(const AbstractMetaType* type, const AbstractMetaType* other) { - if (!type && !other) - return true; - if (!type || !other) - return false; - return type->typeEntry() == other->typeEntry() - && type->isConstant() == other->isConstant() - && type->referenceType() == other->referenceType() - && type->indirections() == other->indirections(); + return (type != nullptr) == (other != nullptr) + && (type == nullptr || *type == *other); } static bool _compareAbstractMetaFunctions(const AbstractMetaFunction* func, const AbstractMetaFunction* other) @@ -1426,7 +1436,7 @@ void AbstractMetaBuilderPrivate::traverseFunctions(ScopeModelItem scopeItem, } } else if (QPropertySpec* reset = metaClass->propertySpecForReset(metaFunction->name())) { // Property resetter must be in the form "void name()" - if ((!metaFunction->type()) && (metaFunction->arguments().size() == 0)) { + if ((!metaFunction->type()) && metaFunction->arguments().isEmpty()) { *metaFunction += AbstractMetaAttributes::PropertyResetter; metaFunction->setPropertySpec(reset); } @@ -1635,7 +1645,7 @@ bool AbstractMetaBuilderPrivate::setupInheritance(AbstractMetaClass *metaClass) return true; } -void AbstractMetaBuilderPrivate::traverseEnums(ScopeModelItem scopeItem, +void AbstractMetaBuilderPrivate::traverseEnums(const ScopeModelItem &scopeItem, AbstractMetaClass *metaClass, const QStringList &enumsDeclarations) { @@ -1724,13 +1734,9 @@ AbstractMetaFunction* AbstractMetaBuilderPrivate::traverseFunction(const AddedFu replacedExpression = metaFunction->replacedDefaultExpression(m_currentClass, i + 1); if (!replacedExpression.isEmpty()) { - QString expr = replacedExpression; if (!metaFunction->removedDefaultExpression(m_currentClass, i + 1)) { - metaArg->setDefaultValueExpression(expr); - metaArg->setOriginalDefaultValueExpression(expr); - - if (metaArg->type()->isEnum() || metaArg->type()->isFlags()) - m_enumDefaultArguments << QPair<AbstractMetaArgument*, AbstractMetaFunction*>(metaArg, metaFunction); + metaArg->setDefaultValueExpression(replacedExpression); + metaArg->setOriginalDefaultValueExpression(replacedExpression); } } } @@ -1784,7 +1790,7 @@ void AbstractMetaBuilderPrivate::fixArgumentNames(AbstractMetaFunction *func, co } } -static QString functionSignature(FunctionModelItem functionItem) +static QString functionSignature(const FunctionModelItem &functionItem) { QStringList args; const ArgumentList &arguments = functionItem->arguments(); @@ -1802,50 +1808,6 @@ static inline QString qualifiedFunctionSignatureWithType(const FunctionModelItem result += functionSignature(functionItem); return result; } - -static inline QString msgUnmatchedParameterType(const ArgumentModelItem &arg, int n) -{ - QString result; - QTextStream str(&result); - str << "unmatched type '" << arg->type().toString() << "' in parameter #" - << (n + 1); - if (!arg->name().isEmpty()) - str << " \"" << arg->name() << '"'; - return result; -} - -static inline QString msgUnmatchedReturnType(const FunctionModelItem &functionItem) -{ - return QLatin1String("unmatched return type '") - + functionItem->type().toString() + QLatin1Char('\''); -} - -static inline QString msgVoidParameterType(const ArgumentModelItem &arg, int n) -{ - QString result; - QTextStream str(&result); - str << "'void' encountered at parameter #" << (n + 1); - if (!arg->name().isEmpty()) - str << " \"" << arg->name() << '"'; - return result; -} - -static QString msgSkippingFunction(const FunctionModelItem &functionItem, - const QString &signature, const QString &why) -{ - QString result; - QTextStream str(&result); - str << "skipping "; - if (functionItem->isAbstract()) - str << "abstract "; - str << "function '" << signature << "', " << why; - if (functionItem->isAbstract()) { - str << "\nThis will lead to compilation errors due to not " - "being able to instantiate the wrapper."; - } - return result; -} - static inline AbstractMetaFunction::FunctionType functionTypeFromCodeModel(CodeModel::FunctionType ft) { AbstractMetaFunction::FunctionType result = AbstractMetaFunction::NormalFunction; @@ -1874,12 +1836,6 @@ static inline AbstractMetaFunction::FunctionType functionTypeFromCodeModel(CodeM return result; } -static inline QString msgCannotSetArrayUsage(const QString &function, int i, const QString &reason) -{ - return function + QLatin1String(": Cannot use parameter ") + QString::number(i + 1) - + QLatin1String(" as an array: ") + reason; -} - bool AbstractMetaBuilderPrivate::setArrayArgumentType(AbstractMetaFunction *func, const FunctionModelItem &functionItem, int i) @@ -1911,12 +1867,40 @@ bool AbstractMetaBuilderPrivate::setArrayArgumentType(AbstractMetaFunction *func return true; } -AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(FunctionModelItem functionItem) +static bool generateExceptionHandling(const AbstractMetaFunction *func, + ExceptionSpecification spec, + TypeSystem::ExceptionHandling handling) +{ + switch (func->functionType()) { + case AbstractMetaFunction::CopyConstructorFunction: + case AbstractMetaFunction::MoveConstructorFunction: + case AbstractMetaFunction::AssignmentOperatorFunction: + case AbstractMetaFunction::MoveAssignmentOperatorFunction: + case AbstractMetaFunction::DestructorFunction: + return false; + default: + break; + } + switch (handling) { + case TypeSystem::ExceptionHandling::On: + return true; + case TypeSystem::ExceptionHandling::AutoDefaultToOn: + return spec != ExceptionSpecification::NoExcept; + case TypeSystem::ExceptionHandling::AutoDefaultToOff: + return spec == ExceptionSpecification::Throws; + default: + break; + } + return false; +} + +AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const FunctionModelItem &functionItem) { if (functionItem->isDeleted() || !functionItem->templateParameters().isEmpty()) return nullptr; QString functionName = functionItem->name(); QString className; + TypeSystem::ExceptionHandling exceptionHandling = TypeSystem::ExceptionHandling::Unspecified; if (m_currentClass) { // Clang: Skip qt_metacast(), qt_metacall(), expanded from Q_OBJECT // and overridden metaObject(), QGADGET helpers @@ -1925,6 +1909,7 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(FunctionModel return nullptr; } className = m_currentClass->typeEntry()->qualifiedCppName(); + exceptionHandling = m_currentClass->typeEntry()->exceptionHandling(); if (functionName == QLatin1String("metaObject") && className != QLatin1String("QObject")) return nullptr; } @@ -1936,13 +1921,16 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(FunctionModel QString rejectReason; if (TypeDatabase::instance()->isFunctionRejected(className, functionName, &rejectReason)) { m_rejectedFunctions.insert(originalQualifiedSignatureWithReturn + rejectReason, AbstractMetaBuilder::GenerationDisabled); - return 0; - } - else if (TypeDatabase::instance()->isFunctionRejected(className, - functionSignature(functionItem), &rejectReason)) { - m_rejectedFunctions.insert(originalQualifiedSignatureWithReturn + rejectReason, AbstractMetaBuilder::GenerationDisabled); - return 0; + return nullptr; } + const QString &signature = functionSignature(functionItem); + const bool rejected = + TypeDatabase::instance()->isFunctionRejected(className, signature, &rejectReason); + qCDebug(lcShiboken).nospace().noquote() << __FUNCTION__ + << ": Checking rejection for signature \"" << signature << "\" for " << className + << ": " << rejected; + if (rejected) + return nullptr; if (functionItem->isFriend()) return 0; @@ -1951,6 +1939,7 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(FunctionModel // Additional check for assignment/move assignment down below metaFunction->setFunctionType(functionTypeFromCodeModel(functionItem->functionType())); metaFunction->setConstant(functionItem->isConstant()); + metaFunction->setExceptionSpecification(functionItem->exceptionSpecification()); if (ReportHandler::isDebug(ReportHandler::MediumDebug)) qCDebug(lcShiboken).noquote().nospace() << " - " << functionName << "()"; @@ -1987,6 +1976,7 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(FunctionModel else *metaFunction += AbstractMetaAttributes::Protected; + QString errorMessage; switch (metaFunction->functionType()) { case AbstractMetaFunction::DestructorFunction: break; @@ -2005,9 +1995,9 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(FunctionModel AbstractMetaType *type = nullptr; if (!returnType.isVoid()) { - type = translateType(returnType); + type = translateType(returnType, true, &errorMessage); if (!type) { - const QString reason = msgUnmatchedReturnType(functionItem); + const QString reason = msgUnmatchedReturnType(functionItem, errorMessage); qCWarning(lcShiboken, "%s", qPrintable(msgSkippingFunction(functionItem, originalQualifiedSignatureWithReturn, reason))); m_rejectedFunctions.insert(originalQualifiedSignatureWithReturn, AbstractMetaBuilder::UnmatchedReturnType); @@ -2033,7 +2023,7 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(FunctionModel AbstractMetaArgumentList metaArguments; for (int i = 0; i < arguments.size(); ++i) { - ArgumentModelItem arg = arguments.at(i); + const ArgumentModelItem &arg = arguments.at(i); if (TypeDatabase::instance()->isArgumentTypeRejected(className, arg->type().toString(), &rejectReason)) { m_rejectedFunctions.insert(originalQualifiedSignatureWithReturn + rejectReason, AbstractMetaBuilder::GenerationDisabled); @@ -2041,7 +2031,7 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(FunctionModel return nullptr; } - AbstractMetaType *metaType = translateType(arg->type()); + AbstractMetaType *metaType = translateType(arg->type(), true, &errorMessage); if (!metaType) { // If an invalid argument has a default value, simply remove it if (arg->defaultValue()) { @@ -2058,18 +2048,7 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(FunctionModel break; } Q_ASSERT(metaType == 0); - const QString reason = msgUnmatchedParameterType(arg, i); - qCWarning(lcShiboken, "%s", - qPrintable(msgSkippingFunction(functionItem, originalQualifiedSignatureWithReturn, reason))); - const QString rejectedFunctionSignature = originalQualifiedSignatureWithReturn - + QLatin1String(": ") + reason; - m_rejectedFunctions.insert(rejectedFunctionSignature, AbstractMetaBuilder::UnmatchedArgumentType); - delete metaFunction; - return nullptr; - } - - if (metaType == Q_NULLPTR) { - const QString reason = msgVoidParameterType(arg, i); + const QString reason = msgUnmatchedParameterType(arg, i, errorMessage); qCWarning(lcShiboken, "%s", qPrintable(msgSkippingFunction(functionItem, originalQualifiedSignatureWithReturn, reason))); const QString rejectedFunctionSignature = originalQualifiedSignatureWithReturn @@ -2089,9 +2068,22 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(FunctionModel metaFunction->setArguments(metaArguments); + const FunctionModificationList functionMods = metaFunction->modifications(m_currentClass); + + for (const FunctionModification &mod : functionMods) { + if (mod.exceptionHandling() != TypeSystem::ExceptionHandling::Unspecified) { + exceptionHandling = mod.exceptionHandling(); + break; + } + } + + metaFunction->setGenerateExceptionHandling(generateExceptionHandling(metaFunction, + functionItem->exceptionSpecification(), + exceptionHandling)); + // Find the correct default values for (int i = 0, size = metaArguments.size(); i < size; ++i) { - ArgumentModelItem arg = arguments.at(i); + const ArgumentModelItem &arg = arguments.at(i); AbstractMetaArgument* metaArg = metaArguments.at(i); //use relace-default-expression for set default value @@ -2099,9 +2091,8 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(FunctionModel if (m_currentClass) { replacedExpression = metaFunction->replacedDefaultExpression(m_currentClass, i + 1); } else { - FunctionModificationList mods = TypeDatabase::instance()->functionModifications(metaFunction->minimalSignature()); - if (!mods.isEmpty()) { - QVector<ArgumentModification> argMods = mods.constFirst().argument_mods; + if (!functionMods.isEmpty()) { + QVector<ArgumentModification> argMods = functionMods.constFirst().argument_mods; if (!argMods.isEmpty()) replacedExpression = argMods.constFirst().replacedDefaultExpression; } @@ -2119,10 +2110,6 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(FunctionModel expr = replacedExpression; } metaArg->setDefaultValueExpression(expr); - - if (metaArg->type()->isEnum() || metaArg->type()->isFlags()) - m_enumDefaultArguments << QPair<AbstractMetaArgument *, AbstractMetaFunction *>(metaArg, metaFunction); - hasDefaultValue = !expr.isEmpty(); } @@ -2140,9 +2127,8 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(FunctionModel } if (!metaArguments.isEmpty()) { - const FunctionModificationList &mods = metaFunction->modifications(m_currentClass); - fixArgumentNames(metaFunction, mods); - for (const FunctionModification &mod : mods) { + fixArgumentNames(metaFunction, functionMods); + for (const FunctionModification &mod : functionMods) { for (const ArgumentModification &argMod : mod.argument_mods) { if (argMod.array) setArrayArgumentType(metaFunction, functionItem, argMod.index - 1); @@ -2202,8 +2188,8 @@ AbstractMetaType *AbstractMetaBuilderPrivate::translateType(const AddedFunction: if (!type) { QStringList candidates; - SingleTypeEntryHash entries = typeDb->entries(); - for (SingleTypeEntryHash::const_iterator it = entries.cbegin(), end = entries.cend(); it != end; ++it) { + const auto &entries = typeDb->entries(); + for (auto it = entries.cbegin(), end = entries.cend(); it != end; ++it) { // Let's try to find the type in different scopes. if (it.key().endsWith(colonColon() + typeName)) candidates.append(it.key()); @@ -2211,15 +2197,17 @@ AbstractMetaType *AbstractMetaBuilderPrivate::translateType(const AddedFunction: QString msg = QStringLiteral("Type '%1' wasn't found in the type database.\n").arg(typeName); - if (candidates.isEmpty()) - qFatal(qPrintable(QString(msg + QLatin1String("Declare it in the type system using the proper <*-type> tag."))), NULL); + if (candidates.isEmpty()) { + qFatal("%sDeclare it in the type system using the proper <*-type> tag.", + qPrintable(msg)); + } msg += QLatin1String("Remember to inform the full qualified name for the type you want to use.\nCandidates are:\n"); candidates.sort(); for (const QString& candidate : qAsConst(candidates)) { msg += QLatin1String(" ") + candidate + QLatin1Char('\n'); } - qFatal(qPrintable(msg), NULL); + qFatal("%s", qPrintable(msg)); } AbstractMetaType *metaType = new AbstractMetaType; @@ -2243,7 +2231,7 @@ static const TypeEntry* findTypeEntryUsingContext(const AbstractMetaClass* metaC { const TypeEntry* type = 0; QStringList context = metaClass->qualifiedCppName().split(colonColon()); - while(!type && (context.size() > 0) ) { + while (!type && !context.isEmpty()) { type = TypeDatabase::instance()->findType(context.join(colonColon()) + colonColon() + qualifiedName); context.removeLast(); } @@ -2251,43 +2239,48 @@ static const TypeEntry* findTypeEntryUsingContext(const AbstractMetaClass* metaC } AbstractMetaType *AbstractMetaBuilderPrivate::translateType(const TypeInfo &_typei, - bool resolveType) + bool resolveType, + QString *errorMessage) +{ + return translateTypeStatic(_typei, m_currentClass, this, resolveType, errorMessage); +} + +AbstractMetaType *AbstractMetaBuilderPrivate::translateTypeStatic(const TypeInfo &_typei, + AbstractMetaClass *currentClass, + AbstractMetaBuilderPrivate *d, + bool resolveType, + QString *errorMessageIn) { // 1. Test the type info without resolving typedefs in case this is present in the // type system - TypeInfo typei; if (resolveType) { - if (AbstractMetaType *resolved = translateType(_typei, false)) + if (AbstractMetaType *resolved = translateTypeStatic(_typei, currentClass, d, false, errorMessageIn)) return resolved; } - if (!resolveType) { - typei = _typei; - } else { + TypeInfo typeInfo = _typei; + if (resolveType) { // Go through all parts of the current scope (including global namespace) // to resolve typedefs. The parser does not properly resolve typedefs in // the global scope when they are referenced from inside a namespace. // This is a work around to fix this bug since fixing it in resolveType // seemed non-trivial - int i = m_scopes.size() - 1; + int i = d ? d->m_scopes.size() - 1 : -1; while (i >= 0) { - typei = TypeInfo::resolveType(_typei, m_scopes.at(i--)); - if (typei.qualifiedName().join(colonColon()) != _typei.qualifiedName().join(colonColon())) + typeInfo = TypeInfo::resolveType(_typei, d->m_scopes.at(i--)); + if (typeInfo.qualifiedName().join(colonColon()) != _typei.qualifiedName().join(colonColon())) break; } } - if (typei.isFunctionPointer()) + if (typeInfo.isFunctionPointer()) { + if (errorMessageIn) + *errorMessageIn = msgUnableToTranslateType(_typei, QLatin1String("Unsupported function pointer.")); return nullptr; + } QString errorMessage; - TypeInfo typeInfo = TypeParser::parse(typei.toString(), &errorMessage); - if (typeInfo.qualifiedName().isEmpty()) { - qWarning().noquote().nospace() << "Unable to translate type \"" << _typei.toString() - << "\": " << errorMessage; - return 0; - } // 2. Handle arrays. // 2.1 Handle char arrays with unspecified size (aka "const char[]") as "const char*" with @@ -2312,16 +2305,22 @@ AbstractMetaType *AbstractMetaBuilderPrivate::translateType(const TypeInfo &_typ if (!typeInfo.arrayElements().isEmpty() && !isConstCharStarCase) { TypeInfo newInfo; //newInfo.setArguments(typeInfo.arguments()); - newInfo.setIndirections(typeInfo.indirections()); + newInfo.setIndirectionsV(typeInfo.indirectionsV()); newInfo.setConstant(typeInfo.isConstant()); + newInfo.setVolatile(typeInfo.isVolatile()); newInfo.setFunctionPointer(typeInfo.isFunctionPointer()); newInfo.setQualifiedName(typeInfo.qualifiedName()); newInfo.setReferenceType(typeInfo.referenceType()); newInfo.setVolatile(typeInfo.isVolatile()); - AbstractMetaType *elementType = translateType(newInfo); - if (!elementType) + AbstractMetaType *elementType = translateTypeStatic(newInfo, currentClass, d, true, &errorMessage); + if (!elementType) { + if (errorMessageIn) { + errorMessage.prepend(QLatin1String("Unable to translate array element: ")); + *errorMessageIn = msgUnableToTranslateType(_typei, errorMessage); + } return nullptr; + } for (int i = typeInfo.arrayElements().size() - 1; i >= 0; --i) { AbstractMetaType *arrayType = new AbstractMetaType; @@ -2329,7 +2328,9 @@ AbstractMetaType *AbstractMetaBuilderPrivate::translateType(const TypeInfo &_typ const QString &arrayElement = typeInfo.arrayElements().at(i); if (!arrayElement.isEmpty()) { bool _ok; - const qint64 elems = findOutValueFromString(arrayElement, _ok); + const qint64 elems = d + ? d->findOutValueFromString(arrayElement, _ok) + : arrayElement.toLongLong(&_ok, 0); if (_ok) arrayType->setArrayElementCount(int(elems)); } @@ -2344,8 +2345,11 @@ AbstractMetaType *AbstractMetaBuilderPrivate::translateType(const TypeInfo &_typ QStringList qualifierList = typeInfo.qualifiedName(); if (qualifierList.isEmpty()) { - qCWarning(lcShiboken).noquote().nospace() - << QStringLiteral("horribly broken type '%1'").arg(_typei.toString()); + errorMessage = msgUnableToTranslateType(_typei, QLatin1String("horribly broken type")); + if (errorMessageIn) + *errorMessageIn = errorMessage; + else + qCWarning(lcShiboken,"%s", qPrintable(errorMessage)); return nullptr; } @@ -2353,19 +2357,21 @@ AbstractMetaType *AbstractMetaBuilderPrivate::translateType(const TypeInfo &_typ QString name = qualifierList.takeLast(); // 4. Special case QFlags (include instantiation in name) - if (qualifiedName == QLatin1String("QFlags")) + if (qualifiedName == QLatin1String("QFlags")) { qualifiedName = typeInfo.toString(); + typeInfo.clearInstantiations(); + } const TypeEntry *type = 0; // 5. Try to find the type // 5.1 - Try first using the current scope - if (m_currentClass) { - type = findTypeEntryUsingContext(m_currentClass, qualifiedName); + if (currentClass) { + type = findTypeEntryUsingContext(currentClass, qualifiedName); // 5.1.1 - Try using the class parents' scopes - if (!type && !m_currentClass->baseClassNames().isEmpty()) { - const AbstractMetaClassList &baseClasses = getBaseClasses(m_currentClass); + if (!type && d && !currentClass->baseClassNames().isEmpty()) { + const AbstractMetaClassList &baseClasses = d->getBaseClasses(currentClass); for (const AbstractMetaClass *cls : baseClasses) { type = findTypeEntryUsingContext(cls, qualifiedName); if (type) @@ -2388,36 +2394,40 @@ AbstractMetaType *AbstractMetaBuilderPrivate::translateType(const TypeInfo &_typ // 8. No? Check if the current class is a template and this type is one // of the parameters. - if (!type && m_currentClass) { - const QVector<TypeEntry *> &template_args = m_currentClass->templateArguments(); + if (!type && currentClass) { + const QVector<TypeEntry *> &template_args = currentClass->templateArguments(); for (TypeEntry *te : template_args) { if (te->name() == qualifiedName) type = te; } } - if (!type) + if (!type) { + if (errorMessageIn) { + *errorMessageIn = + msgUnableToTranslateType(_typei, msgCannotFindTypeEntry(qualifiedName)); + } return nullptr; - - // Used to for diagnostics later... - m_usedTypes << type; + } // These are only implicit and should not appear in code... Q_ASSERT(!type->isInterface()); AbstractMetaType *metaType = new AbstractMetaType; metaType->setTypeEntry(type); - metaType->setIndirections(typeInfo.indirections()); + metaType->setIndirectionsV(typeInfo.indirectionsV()); metaType->setReferenceType(typeInfo.referenceType()); metaType->setConstant(typeInfo.isConstant()); + metaType->setVolatile(typeInfo.isVolatile()); metaType->setOriginalTypeDescription(_typei.toString()); - const auto &templateArguments = typeInfo.arguments(); + const auto &templateArguments = typeInfo.instantiations(); for (int t = 0, size = templateArguments.size(); t < size; ++t) { - TypeInfo ti = templateArguments.at(t); - ti.setQualifiedName(ti.instantiationName()); - AbstractMetaType *targType = translateType(ti); + const TypeInfo &ti = templateArguments.at(t); + AbstractMetaType *targType = translateTypeStatic(ti, currentClass, d, true, &errorMessage); if (!targType) { + if (errorMessageIn) + *errorMessageIn = msgCannotTranslateTemplateArgument(t, ti, errorMessage); delete metaType; return nullptr; } @@ -2434,6 +2444,33 @@ AbstractMetaType *AbstractMetaBuilderPrivate::translateType(const TypeInfo &_typ return metaType; } +AbstractMetaType *AbstractMetaBuilder::translateType(const TypeInfo &_typei, + AbstractMetaClass *currentClass, + bool resolveType, + QString *errorMessage) +{ + return AbstractMetaBuilderPrivate::translateTypeStatic(_typei, currentClass, + nullptr, resolveType, + errorMessage); +} + +AbstractMetaType *AbstractMetaBuilder::translateType(const QString &t, + AbstractMetaClass *currentClass, + bool resolveType, + QString *errorMessageIn) +{ + QString errorMessage; + TypeInfo typeInfo = TypeParser::parse(t, &errorMessage); + if (typeInfo.qualifiedName().isEmpty()) { + errorMessage = msgUnableToTranslateType(t, errorMessage); + if (errorMessageIn) + *errorMessageIn = errorMessage; + else + qCWarning(lcShiboken, "%s", qPrintable(errorMessage)); + return nullptr; + } + return translateType(typeInfo, currentClass, resolveType, errorMessageIn); +} qint64 AbstractMetaBuilderPrivate::findOutValueFromString(const QString &stringValue, bool &ok) { @@ -2472,7 +2509,7 @@ qint64 AbstractMetaBuilderPrivate::findOutValueFromString(const QString &stringV return 0; } -QString AbstractMetaBuilderPrivate::fixDefaultValue(ArgumentModelItem item, +QString AbstractMetaBuilderPrivate::fixDefaultValue(const ArgumentModelItem &item, AbstractMetaType *type, AbstractMetaFunction *fnc, AbstractMetaClass *implementingClass, @@ -2676,59 +2713,55 @@ bool AbstractMetaBuilderPrivate::ancestorHasPrivateCopyConstructor(const Abstrac return false; } -AbstractMetaType* AbstractMetaBuilderPrivate::inheritTemplateType(const QVector<AbstractMetaType *> &templateTypes, - const AbstractMetaType *metaType, - bool *ok) +AbstractMetaType * + AbstractMetaBuilderPrivate::inheritTemplateType(const AbstractMetaTypeList &templateTypes, + const AbstractMetaType *metaType) { - if (ok) - *ok = true; - if (!metaType || (!metaType->typeEntry()->isTemplateArgument() && !metaType->hasInstantiations())) - return metaType ? metaType->copy() : 0; + Q_ASSERT(metaType); + + QScopedPointer<AbstractMetaType> returned(metaType->copy()); - AbstractMetaType *returned = metaType->copy(); - returned->setOriginalTemplateType(metaType->copy()); + if (!metaType->typeEntry()->isTemplateArgument() && !metaType->hasInstantiations()) + return returned.take(); + + returned->setOriginalTemplateType(metaType); if (returned->typeEntry()->isTemplateArgument()) { const TemplateArgumentEntry* tae = static_cast<const TemplateArgumentEntry*>(returned->typeEntry()); // If the template is intantiated with void we special case this as rejecting the functions that use this // parameter from the instantiation. - if (templateTypes.size() <= tae->ordinal() || templateTypes.at(tae->ordinal())->typeEntry()->name() == QLatin1String("void")) { - if (ok) - *ok = false; - return 0; - } + const AbstractMetaType *templateType = templateTypes.value(tae->ordinal()); + if (!templateType || templateType->typeEntry()->isVoid()) + return nullptr; AbstractMetaType* t = returned->copy(); - t->setTypeEntry(templateTypes.at(tae->ordinal())->typeEntry()); - t->setIndirections(templateTypes.at(tae->ordinal())->indirections() + t->indirections() ? 1 : 0); + t->setTypeEntry(templateType->typeEntry()); + t->setIndirections(templateType->indirections() + t->indirections() ? 1 : 0); t->decideUsagePattern(); - delete returned; - returned = inheritTemplateType(templateTypes, t, ok); - if (ok && !(*ok)) - return 0; + return inheritTemplateType(templateTypes, t); } if (returned->hasInstantiations()) { AbstractMetaTypeList instantiations = returned->instantiations(); for (int i = 0; i < instantiations.count(); ++i) { - AbstractMetaType *type = instantiations[i]; - instantiations[i] = inheritTemplateType(templateTypes, type, ok); - if (ok && !(*ok)) - return 0; + instantiations[i] = + inheritTemplateType(templateTypes, instantiations.at(i)); + if (!instantiations.at(i)) + return nullptr; } returned->setInstantiations(instantiations, true); } - return returned; + return returned.take(); } bool AbstractMetaBuilderPrivate::inheritTemplate(AbstractMetaClass *subclass, const AbstractMetaClass *templateClass, const TypeInfo &info) { - QVector<TypeInfo> targs = info.arguments(); + QVector<TypeInfo> targs = info.instantiations(); QVector<AbstractMetaType *> templateTypes; if (subclass->isTypeDef()) { @@ -2743,20 +2776,35 @@ bool AbstractMetaBuilderPrivate::inheritTemplate(AbstractMetaClass *subclass, for (const TypeInfo &i : qAsConst(targs)) { QString typeName = i.qualifiedName().join(colonColon()); - QStringList possibleNames; - possibleNames << subclass->qualifiedCppName() + colonColon() + typeName; - possibleNames << templateClass->qualifiedCppName() + colonColon() + typeName; - if (subclass->enclosingClass()) - possibleNames << subclass->enclosingClass()->qualifiedCppName() + colonColon() + typeName; - possibleNames << typeName; - - TypeDatabase* typeDb = TypeDatabase::instance(); - TypeEntry* t = 0; - QString templateParamName; - for (const QString &possibleName : qAsConst(possibleNames)) { - t = typeDb->findType(possibleName); - if (t) - break; + TypeDatabase *typeDb = TypeDatabase::instance(); + TypeEntry *t = nullptr; + // Check for a non-type template integer parameter, that is, for a base + // "template <int R, int C> Matrix<R, C>" and subclass + // "typedef Matrix<2,3> Matrix2x3;". If so, create dummy entries of + // EnumValueTypeEntry for the integer values encountered on the fly. + const bool isNumber = std::all_of(typeName.cbegin(), typeName.cend(), + [](QChar c) { return c.isDigit(); }); + if (isNumber) { + t = typeDb->findType(typeName); + if (!t) { + t = new EnumValueTypeEntry(typeName, typeName, nullptr, + QVersionNumber(0, 0)); + t->setCodeGeneration(0); + typeDb->addType(t); + } + } else { + QStringList possibleNames; + possibleNames << subclass->qualifiedCppName() + colonColon() + typeName; + possibleNames << templateClass->qualifiedCppName() + colonColon() + typeName; + if (subclass->enclosingClass()) + possibleNames << subclass->enclosingClass()->qualifiedCppName() + colonColon() + typeName; + possibleNames << typeName; + + for (const QString &possibleName : qAsConst(possibleNames)) { + t = typeDb->findType(possibleName); + if (t) + break; + } } if (t) { @@ -2764,48 +2812,48 @@ bool AbstractMetaBuilderPrivate::inheritTemplate(AbstractMetaClass *subclass, temporaryType->setTypeEntry(t); temporaryType->setConstant(i.isConstant()); temporaryType->setReferenceType(i.referenceType()); - temporaryType->setIndirections(i.indirections()); + temporaryType->setIndirectionsV(i.indirectionsV()); temporaryType->decideUsagePattern(); templateTypes << temporaryType; } else { qCWarning(lcShiboken).noquote().nospace() - << "Ignoring template parameter " << templateParamName << " from " - << info.instantiationName() << ", because I don't know what it is."; + << "Ignoring template parameter " << typeName << " from " + << info.toString() << ". The corresponding type was not found in the typesystem."; } } - AbstractMetaFunctionList funcs = subclass->functions(); + const AbstractMetaFunctionList &subclassFuncs = subclass->functions(); const AbstractMetaFunctionList &templateClassFunctions = templateClass->functions(); for (const AbstractMetaFunction *function : templateClassFunctions) { - if (function->isModifiedRemoved(TypeSystem::All)) + // If the function is modified or the instantiation has an equally named + // function we have shadowing, so we need to skip it. + if (function->isModifiedRemoved(TypeSystem::All) + || AbstractMetaFunction::find(subclassFuncs, function->name()) != nullptr) { continue; + } - AbstractMetaFunction *f = function->copy(); + QScopedPointer<AbstractMetaFunction> f(function->copy()); f->setArguments(AbstractMetaArgumentList()); - bool ok = true; - AbstractMetaType *ftype = function->type(); - f->replaceType(inheritTemplateType(templateTypes, ftype, &ok)); - if (!ok) { - delete f; - continue; + if (function->type()) { // Non-void + AbstractMetaType *returnType = inheritTemplateType(templateTypes, function->type()); + if (!returnType) + continue; + f->replaceType(returnType); } const AbstractMetaArgumentList &arguments = function->arguments(); for (AbstractMetaArgument *argument : arguments) { - AbstractMetaType* atype = argument->type(); - - AbstractMetaArgument *arg = argument->copy(); - arg->replaceType(inheritTemplateType(templateTypes, atype, &ok)); - if (!ok) + AbstractMetaType *argType = inheritTemplateType(templateTypes, argument->type()); + if (!argType) break; + AbstractMetaArgument *arg = argument->copy(); + arg->replaceType(argType); f->addArgument(arg); } - if (!ok) { - delete f; + if (f->arguments().size() < function->arguments().size()) continue; - } // There is no base class in the target language to inherit from here, so // the template instantiation is the class that implements the function. @@ -2816,27 +2864,11 @@ bool AbstractMetaBuilderPrivate::inheritTemplate(AbstractMetaClass *subclass, // on the inherited functions. f->setDeclaringClass(subclass); - - if (f->isConstructor() && subclass->isTypeDef()) { + if (f->isConstructor()) { + if (!subclass->isTypeDef()) + continue; f->setName(subclass->name()); f->setOriginalName(subclass->name()); - } else if (f->isConstructor()) { - delete f; - continue; - } - - // if the instantiation has a function named the same as an existing - // function we have shadowing so we need to skip it. - bool found = false; - for (int i = 0; i < funcs.size(); ++i) { - if (funcs.at(i)->name() == f->name()) { - found = true; - continue; - } - } - if (found) { - delete f; - continue; } ComplexTypeEntry* te = subclass->typeEntry(); @@ -2863,7 +2895,27 @@ bool AbstractMetaBuilderPrivate::inheritTemplate(AbstractMetaClass *subclass, te->addFunctionModification(mod); } - subclass->addFunction(f); + subclass->addFunction(f.take()); + } + + const AbstractMetaFieldList &subClassFields = subclass->fields(); + const AbstractMetaFieldList &templateClassFields = templateClass->fields(); + for (const AbstractMetaField *field : templateClassFields) { + // If the field is modified or the instantiation has a field named + // the same as an existing field we have shadowing, so we need to skip it. + if (field->isModifiedRemoved(TypeSystem::All) + || field->attributes().testFlag(AbstractMetaAttributes::Static) + || AbstractMetaField::find(subClassFields, field->name()) != nullptr) { + continue; + } + + QScopedPointer<AbstractMetaField> f(field->copy()); + f->setEnclosingClass(subclass); + AbstractMetaType *fieldType = inheritTemplateType(templateTypes, field->type()); + if (!fieldType) + continue; + f->replaceType(fieldType); + subclass->addField(f.take()); } subclass->setTemplateBaseClass(templateClass); @@ -2878,9 +2930,9 @@ void AbstractMetaBuilderPrivate::parseQ_Property(AbstractMetaClass *metaClass, const QStringList &declarations) { for (int i = 0; i < declarations.size(); ++i) { - QString p = declarations.at(i); + const QString &p = declarations.at(i); - QStringList l = p.split(QLatin1String(" ")); + QStringList l = p.split(QLatin1Char(' ')); QStringList qualifiedScopeName = currentScope()->qualifiedName(); @@ -2925,8 +2977,8 @@ void AbstractMetaBuilderPrivate::parseQ_Property(AbstractMetaClass *metaClass, static AbstractMetaFunction* findCopyCtor(AbstractMetaClass* cls) { - AbstractMetaFunctionList functions = cls->queryFunctions(AbstractMetaClass::Invisible); - functions << cls->queryFunctions(AbstractMetaClass::Visible); + + const auto &functions = cls->functions(); for (AbstractMetaFunction *f : qAsConst(functions)) { const AbstractMetaFunction::FunctionType t = f->functionType(); diff --git a/sources/shiboken2/ApiExtractor/abstractmetabuilder.h b/sources/shiboken2/ApiExtractor/abstractmetabuilder.h index a0ca71b94..01806f6b4 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetabuilder.h +++ b/sources/shiboken2/ApiExtractor/abstractmetabuilder.h @@ -38,7 +38,10 @@ QT_FORWARD_DECLARE_CLASS(QIODevice) class AbstractMetaBuilderPrivate; class AbstractMetaClass; +class AbstractMetaType; class AbstractMetaEnumValue; +class TypeInfo; +class TypeEntry; class AbstractMetaBuilder { @@ -61,6 +64,7 @@ public: AbstractMetaClassList smartPointers() const; AbstractMetaFunctionList globalFunctions() const; AbstractMetaEnumList globalEnums() const; + AbstractMetaEnum *findEnum(const TypeEntry *typeEntry) const; /** * Sorts a list of classes topologically, if an AbstractMetaClass object @@ -83,6 +87,16 @@ public: */ void setGlobalHeader(const QString& globalHeader); + static AbstractMetaType *translateType(const TypeInfo &_typei, + AbstractMetaClass *currentClass = nullptr, + bool resolveType = true, + QString *errorMessage = nullptr); + static AbstractMetaType *translateType(const QString &t, + AbstractMetaClass *currentClass = nullptr, + bool resolveType = true, + QString *errorMessage = nullptr); + + #ifndef QT_NO_DEBUG_STREAM void formatDebug(QDebug &d) const; #endif diff --git a/sources/shiboken2/ApiExtractor/abstractmetabuilder_p.h b/sources/shiboken2/ApiExtractor/abstractmetabuilder_p.h index 59e3cfc94..ec55d1b47 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetabuilder_p.h +++ b/sources/shiboken2/ApiExtractor/abstractmetabuilder_p.h @@ -60,11 +60,12 @@ public: ScopeModelItem currentScope() const { return m_scopes.constLast(); } - AbstractMetaClass *argumentToClass(ArgumentModelItem); + AbstractMetaClass *argumentToClass(const ArgumentModelItem &); void addAbstractMetaClass(AbstractMetaClass *cls); AbstractMetaClass *traverseTypeDef(const FileModelItem &dom, const TypeDefModelItem &typeDef); + void traverseTypesystemTypedefs(); AbstractMetaClass *traverseClass(const FileModelItem &dom, const ClassModelItem &item); AbstractMetaClass *currentTraversedClass(ScopeModelItem item); @@ -74,9 +75,9 @@ public: bool setupInheritance(AbstractMetaClass *metaClass); AbstractMetaClass *traverseNamespace(const FileModelItem &dom, const NamespaceModelItem &item); - AbstractMetaEnum *traverseEnum(EnumModelItem item, AbstractMetaClass *enclosing, + AbstractMetaEnum *traverseEnum(const EnumModelItem &item, AbstractMetaClass *enclosing, const QSet<QString> &enumsDeclarations); - void traverseEnums(ScopeModelItem item, AbstractMetaClass *parent, + void traverseEnums(const ScopeModelItem &item, AbstractMetaClass *parent, const QStringList &enumsDeclarations); AbstractMetaFunctionList classFunctionList(const ScopeModelItem &scopeItem, bool *constructorRejected); @@ -85,18 +86,18 @@ public: bool *constructorRejected); void traverseFunctions(ScopeModelItem item, AbstractMetaClass *parent); void applyFunctionModifications(AbstractMetaFunction* func); - void traverseFields(ScopeModelItem item, AbstractMetaClass *parent); - void traverseStreamOperator(FunctionModelItem functionItem); - void traverseOperatorFunction(FunctionModelItem item); + void traverseFields(const ScopeModelItem &item, AbstractMetaClass *parent); + void traverseStreamOperator(const FunctionModelItem &functionItem); + void traverseOperatorFunction(const FunctionModelItem &item); AbstractMetaFunction* traverseFunction(const AddedFunction &addedFunc); AbstractMetaFunction* traverseFunction(const AddedFunction &addedFunc, AbstractMetaClass *metaClass); - AbstractMetaFunction *traverseFunction(FunctionModelItem function); - AbstractMetaField *traverseField(VariableModelItem field, + AbstractMetaFunction *traverseFunction(const FunctionModelItem &function); + AbstractMetaField *traverseField(const VariableModelItem &field, const AbstractMetaClass *cls); void checkFunctionModifications(); - void registerHashFunction(FunctionModelItem functionItem); - void registerToStringCapability(FunctionModelItem functionItem); + void registerHashFunction(const FunctionModelItem &functionItem); + void registerToStringCapability(const FunctionModelItem &functionItem); /** * A conversion operator function should not have its owner class as @@ -118,12 +119,19 @@ public: void setupFunctionDefaults(AbstractMetaFunction *metaFunction, AbstractMetaClass *metaClass); - QString fixDefaultValue(ArgumentModelItem item, AbstractMetaType *type, + QString fixDefaultValue(const ArgumentModelItem &item, AbstractMetaType *type, AbstractMetaFunction *fnc, AbstractMetaClass *, int argumentIndex); AbstractMetaType *translateType(const AddedFunction::TypeInfo &typeInfo); AbstractMetaType *translateType(const TypeInfo &type, - bool resolveType = true); + bool resolveType = true, + QString *errorMessage = nullptr); + static AbstractMetaType *translateTypeStatic(const TypeInfo &type, + AbstractMetaClass *current, + AbstractMetaBuilderPrivate *d = nullptr, + bool resolveType = true, + QString *errorMessageIn = nullptr); + qint64 findOutValueFromString(const QString &stringValue, bool &ok); AbstractMetaClass *findTemplateClass(const QString& name, const AbstractMetaClass *context, @@ -135,9 +143,8 @@ public: bool inheritTemplate(AbstractMetaClass *subclass, const AbstractMetaClass *templateClass, const TypeInfo &info); - AbstractMetaType *inheritTemplateType(const QVector<AbstractMetaType *> &templateTypes, - const AbstractMetaType *metaType, - bool *ok = Q_NULLPTR); + AbstractMetaType *inheritTemplateType(const AbstractMetaTypeList &templateTypes, + const AbstractMetaType *metaType); bool isQObject(const FileModelItem &dom, const QString &qualifiedName); bool isEnum(const FileModelItem &dom, const QStringList &qualifiedName); @@ -161,8 +168,6 @@ public: AbstractMetaFunctionList m_globalFunctions; AbstractMetaEnumList m_globalEnums; - QSet<const TypeEntry *> m_usedTypes; - typedef QMap<QString, AbstractMetaBuilder::RejectReason> RejectMap; RejectMap m_rejectedClasses; @@ -170,11 +175,7 @@ public: RejectMap m_rejectedFunctions; RejectMap m_rejectedFields; - QList<AbstractMetaEnum *> m_enums; - - QList<QPair<AbstractMetaArgument *, AbstractMetaFunction *> > m_enumDefaultArguments; - - QHash<QString, AbstractMetaEnumValue *> m_enumValues; + QHash<const TypeEntry *, AbstractMetaEnum *> m_enums; AbstractMetaClass *m_currentClass; QList<ScopeModelItem> m_scopes; diff --git a/sources/shiboken2/ApiExtractor/abstractmetalang.cpp b/sources/shiboken2/ApiExtractor/abstractmetalang.cpp index 5be7050bf..c65d7e0bd 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetalang.cpp +++ b/sources/shiboken2/ApiExtractor/abstractmetalang.cpp @@ -27,10 +27,13 @@ ****************************************************************************/ #include "abstractmetalang.h" +#include "messages.h" #include "reporthandler.h" #include "typedatabase.h" #include "typesystem.h" +#include <parser/codemodel.h> + #ifndef QT_NO_DEBUG_STREAM # include <QtCore/QMetaEnum> # include <QtCore/QMetaObject> @@ -55,6 +58,16 @@ QDebug operator<<(QDebug d, const AbstractMetaAttributes *aa) } #endif // !QT_NO_DEBUG_STREAM +template <class MetaClass> +MetaClass *findByName(QVector<MetaClass *> haystack, QStringView needle) +{ + for (MetaClass *c : haystack) { + if (c->name() == needle) + return c; + } + return nullptr; +} + /******************************************************************************* * AbstractMetaVariable */ @@ -112,8 +125,8 @@ void AbstractMetaAttributes::assignMetaAttributes(const AbstractMetaAttributes & AbstractMetaType::AbstractMetaType() : m_constant(false), + m_volatile(false), m_cppInstantiation(true), - m_indirections(0), m_reserved(0) { } @@ -155,8 +168,9 @@ AbstractMetaType *AbstractMetaType::copy() const cpy->setTypeUsagePattern(typeUsagePattern()); cpy->setConstant(isConstant()); + cpy->setVolatile(isVolatile()); cpy->setReferenceType(referenceType()); - cpy->setIndirections(indirections()); + cpy->setIndirectionsV(indirectionsV()); cpy->setInstantiations(instantiations()); cpy->setArrayElementCount(arrayElementCount()); cpy->setOriginalTypeDescription(originalTypeDescription()); @@ -278,6 +292,26 @@ bool AbstractMetaType::hasTemplateChildren() const return false; } +bool AbstractMetaType::equals(const AbstractMetaType &rhs) const +{ + if (m_typeEntry != rhs.m_typeEntry || m_constant != rhs.m_constant + || m_referenceType != rhs.m_referenceType + || m_indirections != rhs.m_indirections + || m_instantiations.size() != rhs.m_instantiations.size() + || m_arrayElementCount != rhs.m_arrayElementCount) { + return false; + } + if ((m_arrayElementType != nullptr) != (rhs.m_arrayElementType != nullptr) + || (m_arrayElementType != nullptr && !m_arrayElementType->equals(*rhs.m_arrayElementType))) { + return false; + } + for (int i = 0, size = m_instantiations.size(); i < size; ++i) { + if (!m_instantiations.at(i)->equals(*rhs.m_instantiations.at(i))) + return false; + } + return true; +} + #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug d, const AbstractMetaType *at) { @@ -291,16 +325,32 @@ QDebug operator<<(QDebug d, const AbstractMetaType *at) d << ", typeEntry=" << at->typeEntry() << ", signature=\"" << at->cppSignature() << "\", pattern=" << at->typeUsagePattern(); - if (at->indirections()) - d << ", indirections=" << at->indirections(); + const auto indirections = at->indirectionsV(); + if (!indirections.isEmpty()) { + d << ", indirections="; + for (auto i : indirections) + d << ' ' << TypeInfo::indirectionKeyword(i); + } if (at->referenceType()) d << ", reftype=" << at->referenceType(); if (at->isConstant()) d << ", [const]"; + if (at->isVolatile()) + d << ", [volatile]"; if (at->isArray()) { d << ", array of \"" << at->arrayElementType()->cppSignature() << "\", arrayElementCount=" << at->arrayElementCount(); } + const auto &instantiations = at->instantiations(); + if (const int instantiationsSize = instantiations.size()) { + d << ", instantiations[" << instantiationsSize << "]=<"; + for (int i = 0; i < instantiationsSize; ++i) { + if (i) + d << ", "; + d << instantiations.at(i); + } + } + d << '>'; } } else { d << '0'; @@ -357,7 +407,8 @@ AbstractMetaFunction::AbstractMetaFunction() m_userAdded(false), m_explicit(false), m_pointerOperator(false), - m_isCallOperator(false) + m_isCallOperator(false), + m_generateExceptionHandling(false) { } @@ -475,6 +526,8 @@ AbstractMetaFunction *AbstractMetaFunction::copy() const if (type()) cpy->setType(type()->copy()); cpy->setConstant(isConstant()); + cpy->setExceptionSpecification(m_exceptionSpecification); + cpy->setGenerateExceptionHandling(m_generateExceptionHandling); for (AbstractMetaArgument *arg : m_arguments) cpy->addArgument(arg->copy()); @@ -504,18 +557,17 @@ QStringList AbstractMetaFunction::introspectionCompatibleSignatures(const QStrin if (arguments.size() == resolvedArguments.size()) { QString signature = name() + QLatin1Char('(') + resolvedArguments.join(QLatin1Char(',')) + QLatin1Char(')'); return QStringList(TypeDatabase::normalizedSignature(signature)); - } else { - QStringList returned; - - AbstractMetaArgument *argument = arguments.at(resolvedArguments.size()); - QStringList minimalTypeSignature = argument->type()->minimalSignature().split(QLatin1String("::")); - for (int i = 0; i < minimalTypeSignature.size(); ++i) { - returned += introspectionCompatibleSignatures(QStringList(resolvedArguments) - << QStringList(minimalTypeSignature.mid(minimalTypeSignature.size() - i - 1)).join(QLatin1String("::"))); - } + } + QStringList returned; - return returned; + AbstractMetaArgument *argument = arguments.at(resolvedArguments.size()); + QStringList minimalTypeSignature = argument->type()->minimalSignature().split(QLatin1String("::")); + for (int i = 0; i < minimalTypeSignature.size(); ++i) { + returned += introspectionCompatibleSignatures(QStringList(resolvedArguments) + << QStringList(minimalTypeSignature.mid(minimalTypeSignature.size() - i - 1)).join(QLatin1String("::"))); } + + return returned; } QString AbstractMetaFunction::signature() const @@ -674,38 +726,54 @@ bool AbstractMetaFunction::argumentRemoved(int key) const return false; } -bool AbstractMetaFunction::isVirtualSlot() const +bool AbstractMetaFunction::isDeprecated() const { const FunctionModificationList &modifications = this->modifications(declaringClass()); for (const FunctionModification &modification : modifications) { - if (modification.isVirtualSlot()) + if (modification.isDeprecated()) return true; } - return false; } -bool AbstractMetaFunction::isDeprecated() const +// Auto-detect whether a function should be wrapped into +// Py_BEGIN_ALLOW_THREADS/Py_END_ALLOW_THREADS, that is, temporarily release +// the GIL (global interpreter lock). Doing so is required for any thread-wait +// functions, anything that might call a virtual function (potentially +// reimplemented in Python), and recommended for lengthy I/O or similar. +// It has performance costs, though. +bool AbstractMetaFunction::autoDetectAllowThread() const { - const FunctionModificationList &modifications = this->modifications(declaringClass()); - for (const FunctionModification &modification : modifications) { - if (modification.isDeprecated()) - return true; - } - return false; + // Disallow for simple getter functions. + const bool maybeGetter = m_constant != 0 && m_type != nullptr + && m_arguments.isEmpty(); + return !maybeGetter; } bool AbstractMetaFunction::allowThread() const { - const FunctionModificationList &modifications = this->modifications(declaringClass()); - for (const FunctionModification &modification : modifications) { - if (modification.allowThread()) - return true; + using AllowThread = TypeSystem::AllowThread; + + if (m_cachedAllowThread < 0) { + AllowThread allowThread = AllowThread::Auto; + // Find a modification that specifies allowThread + const FunctionModificationList &modifications = this->modifications(declaringClass()); + for (const FunctionModification &modification : modifications) { + if (modification.allowThread() != AllowThread::Unspecified) { + allowThread = modification.allowThread(); + break; + } + } + + m_cachedAllowThread = allowThread == AllowThread::Allow + || (allowThread == AllowThread::Auto && autoDetectAllowThread()) ? 1 : 0; + + if (m_cachedAllowThread == 0) + qCDebug(lcShiboken).noquote() << msgDisallowThread(this); } - return false; + return m_cachedAllowThread > 0; } - TypeSystem::Ownership AbstractMetaFunction::ownership(const AbstractMetaClass *cls, TypeSystem::Language language, int key) const { const FunctionModificationList &modifications = this->modifications(cls); @@ -751,6 +819,18 @@ QString AbstractMetaFunction::typeReplaced(int key) const return QString(); } +bool AbstractMetaFunction::isModifiedToArray(int argumentIndex) const +{ + const FunctionModificationList &modifications = this->modifications(declaringClass()); + for (const FunctionModification &modification : modifications) { + for (const ArgumentModification &argumentModification : modification.argument_mods) { + if (argumentModification.index == argumentIndex && argumentModification.array != 0) + return true; + } + } + return false; +} + QString AbstractMetaFunction::minimalSignature() const { if (!m_cachedMinimalSignature.isEmpty()) @@ -809,8 +889,9 @@ FunctionModificationList AbstractMetaFunction::modifications(const AbstractMetaC while (implementor) { mods += implementor->typeEntry()->functionModifications(minimalSignature()); if ((implementor == implementor->baseClass()) || - (implementor == implementingClass() && (mods.size() > 0))) + (implementor == implementingClass() && !mods.isEmpty())) { break; + } const AbstractMetaClassList &interfaces = implementor->interfaces(); for (const AbstractMetaClass *interface : interfaces) mods += this->modifications(interface); @@ -873,14 +954,24 @@ bool AbstractMetaFunction::hasSignatureModifications() const return false; } -bool AbstractMetaFunction::isConversionOperator(QString funcName) +bool AbstractMetaFunction::isConversionOperator(const QString& funcName) { static const QRegularExpression opRegEx(QStringLiteral("^operator(?:\\s+(?:const|volatile))?\\s+(\\w+\\s*)&?$")); Q_ASSERT(opRegEx.isValid()); return opRegEx.match(funcName).hasMatch(); } -bool AbstractMetaFunction::isOperatorOverload(QString funcName) +ExceptionSpecification AbstractMetaFunction::exceptionSpecification() const +{ + return m_exceptionSpecification; +} + +void AbstractMetaFunction::setExceptionSpecification(ExceptionSpecification e) +{ + m_exceptionSpecification = e; +} + +bool AbstractMetaFunction::isOperatorOverload(const QString& funcName) { if (isConversionOperator(funcName)) return true; @@ -1038,6 +1129,13 @@ bool function_sorter(AbstractMetaFunction *a, AbstractMetaFunction *b) return a->signature() < b->signature(); } +AbstractMetaFunction * +AbstractMetaFunction::find(const AbstractMetaFunctionList &haystack, + const QString &needle) +{ + return findByName(haystack, needle); +} + #ifndef QT_NO_DEBUG_STREAM static inline void formatMetaFunctionBrief(QDebug &d, const AbstractMetaFunction *af) { @@ -1046,7 +1144,20 @@ static inline void formatMetaFunctionBrief(QDebug &d, const AbstractMetaFunction void AbstractMetaFunction::formatDebugVerbose(QDebug &d) const { - d << m_functionType << ' ' << m_type << ' ' << m_name << '('; + d << m_functionType << ' ' << m_type << ' ' << m_name; + switch (m_exceptionSpecification) { + case ExceptionSpecification::Unknown: + break; + case ExceptionSpecification::NoExcept: + d << " noexcept"; + break; + case ExceptionSpecification::Throws: + d << " throw(...)"; + break; + } + if (m_generateExceptionHandling) + d << "[generate-exception-handling]"; + d << '('; for (int i = 0, count = m_arguments.size(); i < count; ++i) { if (i) d << ", "; @@ -1106,7 +1217,6 @@ AbstractMetaClass::AbstractMetaClass() : m_hasVirtuals(false), m_isPolymorphic(false), m_hasNonpublic(false), - m_hasVirtualSlots(false), m_hasNonPrivateConstructor(false), m_hasPrivateConstructor(false), m_functionsFixed(false), @@ -1330,11 +1440,8 @@ void AbstractMetaClass::setFunctions(const AbstractMetaFunctionList &functions) for (AbstractMetaFunction *f : qAsConst(m_functions)) { f->setOwnerClass(this); - - m_hasVirtualSlots = m_hasVirtualSlots || f->isVirtualSlot(); - m_hasVirtuals = m_hasVirtuals || f->isVirtualSlot() || hasVirtualDestructor(); - m_isPolymorphic = m_isPolymorphic || m_hasVirtuals; - m_hasNonpublic = m_hasNonpublic || !f->isPublic(); + if (!f->isPublic()) + m_hasNonpublic = true; } } @@ -1368,8 +1475,7 @@ void AbstractMetaClass::addFunction(AbstractMetaFunction *function) else Q_ASSERT(false); //memory leak - m_hasVirtualSlots |= function->isVirtualSlot(); - m_hasVirtuals |= function->isVirtual() || function->isVirtualSlot() || hasVirtualDestructor(); + m_hasVirtuals |= function->isVirtual() || hasVirtualDestructor(); m_isPolymorphic |= m_hasVirtuals; m_hasNonpublic |= !function->isPublic(); } @@ -1432,11 +1538,7 @@ bool AbstractMetaClass::hasFunction(const QString &str) const const AbstractMetaFunction* AbstractMetaClass::findFunction(const QString& functionName) const { - for (const AbstractMetaFunction *f : m_functions) { - if (f->name() == functionName) - return f; - } - return 0; + return AbstractMetaFunction::find(m_functions, functionName); } bool AbstractMetaClass::hasProtectedFunctions() const @@ -1511,6 +1613,13 @@ void AbstractMetaClass::setTemplateBaseClassInstantiations(AbstractMetaTypeList& metaClassBaseTemplateInstantiations()->insert(this, instantiations); } +// Does any of the base classes require deletion in the main thread? +bool AbstractMetaClass::deleteInMainThread() const +{ + return typeEntry()->deleteInMainThread() + || (m_baseClass && m_baseClass->deleteInMainThread()); +} + static bool functions_contains(const AbstractMetaFunctionList &l, const AbstractMetaFunction *func) { for (const AbstractMetaFunction *f : l) { @@ -1537,6 +1646,11 @@ AbstractMetaField *AbstractMetaField::copy() const return returned; } +AbstractMetaField *AbstractMetaField::find(const AbstractMetaFieldList &haystack, + const QString &needle) +{ + return findByName(haystack, needle); +} /******************************************************************************* * Indicates that this field has a modification that removes it */ @@ -1723,27 +1837,22 @@ QDebug operator<<(QDebug d, const AbstractMetaEnum *ae) bool AbstractMetaClass::hasConstructors() const { - return queryFunctions(Constructors).size(); + return AbstractMetaClass::queryFirstFunction(m_functions, Constructors) != nullptr; } -bool AbstractMetaClass::hasCopyConstructor() const +const AbstractMetaFunction *AbstractMetaClass::copyConstructor() const { - const AbstractMetaFunctionList &ctors = queryFunctions(Constructors); - for (const AbstractMetaFunction* ctor : ctors) { - if (ctor->functionType() == AbstractMetaFunction::CopyConstructorFunction) - return true; + for (const AbstractMetaFunction *f : m_functions) { + if (f->functionType() == AbstractMetaFunction::CopyConstructorFunction) + return f; } - return false; + return nullptr; } bool AbstractMetaClass::hasPrivateCopyConstructor() const { - const AbstractMetaFunctionList &ctors = queryFunctions(Constructors); - for (const AbstractMetaFunction *ctor : ctors) { - if (ctor->functionType() == AbstractMetaFunction::CopyConstructorFunction && ctor->isPrivate()) - return true; - } - return false; + const AbstractMetaFunction *copyCt = copyConstructor(); + return copyCt && copyCt->isPrivate(); } void AbstractMetaClass::addDefaultConstructor() @@ -1801,85 +1910,122 @@ bool AbstractMetaClass::hasFunction(const AbstractMetaFunction *f) const return functions_contains(m_functions, f); } +bool AbstractMetaClass::generateExceptionHandling() const +{ + return queryFirstFunction(m_functions, AbstractMetaClass::Visible + | AbstractMetaClass::GenerateExceptionHandling) != nullptr; +} /* Goes through the list of functions and returns a list of all functions matching all of the criteria in \a query. */ -AbstractMetaFunctionList AbstractMetaClass::queryFunctions(FunctionQueryOptions query) const +bool AbstractMetaClass::queryFunction(const AbstractMetaFunction *f, FunctionQueryOptions query) { - AbstractMetaFunctionList functions; - - for (AbstractMetaFunction *f : m_functions) { - if ((query & NotRemovedFromTargetLang) && f->isRemovedFrom(f->implementingClass(), TypeSystem::TargetLangCode)) - continue; + if ((query & NotRemovedFromTargetLang) + && f->isRemovedFrom(f->implementingClass(), TypeSystem::TargetLangCode)) { + return false; + } - if ((query & NotRemovedFromTargetLang) && f->isVirtual() && f->isRemovedFrom(f->declaringClass(), TypeSystem::TargetLangCode)) - continue; + if ((query & NotRemovedFromTargetLang) && f->isVirtual() + && f->isRemovedFrom(f->declaringClass(), TypeSystem::TargetLangCode)) { + return false; + } - if ((query & Visible) && f->isPrivate()) - continue; + if ((query & Visible) && f->isPrivate()) + return false; - if ((query & VirtualInTargetLangFunctions) && f->isFinalInTargetLang()) - continue; + if ((query & VirtualInTargetLangFunctions) && f->isFinalInTargetLang()) + return false; - if ((query & Invisible) && !f->isPrivate()) - continue; + if ((query & Invisible) && !f->isPrivate()) + return false; - if ((query & Empty) && !f->isEmptyFunction()) - continue; + if ((query & Empty) && !f->isEmptyFunction()) + return false; - if ((query & WasPublic) && !f->wasPublic()) - continue; + if ((query & WasPublic) && !f->wasPublic()) + return false; - if ((query & ClassImplements) && f->ownerClass() != f->implementingClass()) - continue; + if ((query & ClassImplements) && f->ownerClass() != f->implementingClass()) + return false; - if ((query & FinalInTargetLangFunctions) && !f->isFinalInTargetLang()) - continue; + if ((query & FinalInTargetLangFunctions) && !f->isFinalInTargetLang()) + return false; - if ((query & VirtualInCppFunctions) && !f->isVirtual()) - continue; + if ((query & VirtualInCppFunctions) && !f->isVirtual()) + return false; - if ((query & Signals) && (!f->isSignal())) - continue; + if ((query & Signals) && (!f->isSignal())) + return false; - if ((query & Constructors) && (!f->isConstructor() || f->ownerClass() != f->implementingClass())) - continue; + if ((query & Constructors) && (!f->isConstructor() || f->ownerClass() != f->implementingClass())) + return false; - if (!(query & Constructors) && f->isConstructor()) - continue; + if (!(query & Constructors) && f->isConstructor()) + return false; - // Destructors are never included in the functions of a class currently - /* + // Destructors are never included in the functions of a class currently + /* if ((query & Destructors) && (!f->isDestructor() || f->ownerClass() != f->implementingClass()) || f->isDestructor() && (query & Destructors) == 0) { - continue; + return false; }*/ - if ((query & StaticFunctions) && (!f->isStatic() || f->isSignal())) - continue; + if ((query & StaticFunctions) && (!f->isStatic() || f->isSignal())) + return false; - if ((query & NonStaticFunctions) && (f->isStatic())) - continue; + if ((query & NonStaticFunctions) && (f->isStatic())) + return false; - if ((query & NormalFunctions) && (f->isSignal())) - continue; + if ((query & NormalFunctions) && (f->isSignal())) + return false; - if ((query & OperatorOverloads) && !f->isOperatorOverload()) - continue; + if ((query & OperatorOverloads) && !f->isOperatorOverload()) + return false; + + if ((query & GenerateExceptionHandling) && !f->generateExceptionHandling()) + return false; + + return true; +} - functions << f; +AbstractMetaFunctionList AbstractMetaClass::queryFunctionList(const AbstractMetaFunctionList &list, + FunctionQueryOptions query) +{ + AbstractMetaFunctionList result; + for (AbstractMetaFunction *f : list) { + if (queryFunction(f, query)) + result.append(f); + } + return result; +} + +const AbstractMetaFunction *AbstractMetaClass::queryFirstFunction(const AbstractMetaFunctionList &list, + FunctionQueryOptions query) +{ + AbstractMetaFunctionList result; + for (AbstractMetaFunction *f : list) { + if (queryFunction(f, query)) + return f; } + return nullptr; +} - return functions; +AbstractMetaFunctionList AbstractMetaClass::queryFunctions(FunctionQueryOptions query) const +{ + return AbstractMetaClass::queryFunctionList(m_functions, query); } bool AbstractMetaClass::hasSignals() const { - return cppSignalFunctions().size() > 0; + return queryFirstFunction(m_functions, Signals | Visible | NotRemovedFromTargetLang) != nullptr; } +AbstractMetaFunctionList AbstractMetaClass::cppSignalFunctions() const +{ + return queryFunctions(Signals | Visible | NotRemovedFromTargetLang); +} /** * Adds the specified interface to this class by adding all the @@ -1932,13 +2078,15 @@ void AbstractMetaClass::setInterfaces(const AbstractMetaClassList &interfaces) } } +AbstractMetaField *AbstractMetaClass::findField(const QString &name) const +{ + return AbstractMetaField::find(m_fields, name); +} AbstractMetaEnum *AbstractMetaClass::findEnum(const QString &enumName) { - for (AbstractMetaEnum *e : qAsConst(m_enums)) { - if (e->name() == enumName) - return e; - } + if (AbstractMetaEnum *e = findByName(m_enums, enumName)) + return e; if (typeEntry()->designatedInterface()) return extractInterface()->findEnum(enumName); @@ -2002,8 +2150,8 @@ void AbstractMetaClass::fixFunctions() { if (m_functionsFixed) return; - else - m_functionsFixed = true; + + m_functionsFixed = true; AbstractMetaClass *superClass = baseClass(); AbstractMetaFunctionList funcs = functions(); @@ -2046,8 +2194,7 @@ void AbstractMetaClass::fixFunctions() // we generally don't care about private functions, but we have to get the ones that are // virtual in case they override abstract functions. bool add = (sf->isNormal() || sf->isSignal() || sf->isEmptyFunction()); - for (int fi = 0; fi < funcs.size(); ++fi) { - AbstractMetaFunction *f = funcs.at(fi); + for (AbstractMetaFunction *f : funcs) { if (f->isRemovedFromAllLanguages(f->implementingClass())) continue; @@ -2114,7 +2261,8 @@ void AbstractMetaClass::fixFunctions() if (mod.isNonFinal()) { hasNonFinalModifier = true; break; - } else if (mod.isPrivate()) { + } + if (mod.isPrivate()) { isBaseImplPrivate = true; break; } @@ -2222,6 +2370,8 @@ QString AbstractMetaType::formatSignature(bool minimal) const QString result; if (isConstant()) result += QLatin1String("const "); + if (isVolatile()) + result += QLatin1String("volatile "); if (isArray()) { // Build nested array dimensions a[2][3] in correct order result += m_arrayElementType->minimalSignature(); @@ -2245,10 +2395,10 @@ QString AbstractMetaType::formatSignature(bool minimal) const result += QLatin1String(" >"); } - if (!minimal && (m_indirections != 0 || m_referenceType != NoReference)) + if (!minimal && (!m_indirections.isEmpty() || m_referenceType != NoReference)) result += QLatin1Char(' '); - if (m_indirections) - result += QString(m_indirections, QLatin1Char('*')); + for (Indirection i : m_indirections) + result += TypeInfo::indirectionKeyword(i); switch (referenceType()) { case NoReference: break; @@ -2376,6 +2526,8 @@ QDebug operator<<(QDebug d, const AbstractMetaClass *ac) d << " [final]"; if (ac->m_baseClass) d << ", inherits \"" << ac->m_baseClass->name() << '"'; + if (ac->m_templateBaseClass) + d << ", inherits template \"" << ac->m_templateBaseClass->name() << '"'; const AbstractMetaEnumList &enums = ac->enums(); if (!enums.isEmpty()) d << ", enums[" << enums.size() << "]=" << enums; @@ -2406,6 +2558,18 @@ QDebug operator<<(QDebug d, const AbstractMetaClass *ac) } d << ')'; } + const auto &templateArguments = ac->templateArguments(); + if (const int count = templateArguments.size()) { + d << ", templateArguments=[" << count << "]("; + for (int i = 0; i < count; ++i) { + if (i) + d << ", "; + d << templateArguments.at(i); + } + d << ')'; + } + + } else { d << '0'; } diff --git a/sources/shiboken2/ApiExtractor/abstractmetalang.h b/sources/shiboken2/ApiExtractor/abstractmetalang.h index d1a0fbf88..aaefa32d5 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetalang.h +++ b/sources/shiboken2/ApiExtractor/abstractmetalang.h @@ -288,6 +288,7 @@ class AbstractMetaType { Q_GADGET public: + typedef QVector<Indirection> Indirections; enum TypeUsagePattern { InvalidPattern, @@ -436,6 +437,9 @@ public: m_constant = constant; } + bool isVolatile() const { return m_volatile; } + void setVolatile(bool v) { m_volatile = v; } + bool isConstRef() const; ReferenceType referenceType() const { return m_referenceType; } @@ -443,16 +447,21 @@ public: int actualIndirections() const { - return m_indirections + (m_referenceType == LValueReference ? 1 : 0); - } - int indirections() const - { - return m_indirections; + return m_indirections.size() + (m_referenceType == LValueReference ? 1 : 0); } + + Indirections indirectionsV() const { return m_indirections; } + void setIndirectionsV(const Indirections &i) { m_indirections = i; } + void clearIndirections() { m_indirections.clear(); } + + // "Legacy"? + int indirections() const { return m_indirections.size(); } void setIndirections(int indirections) { - m_indirections = indirections; + m_indirections = Indirections(indirections, Indirection::Pointer); } + void addIndirection(Indirection i = Indirection::Pointer) + { m_indirections.append(i); } void setArrayElementCount(int n) { @@ -527,6 +536,8 @@ public: bool hasTemplateChildren() const; + bool equals(const AbstractMetaType &rhs) const; + private: TypeUsagePattern determineUsagePattern() const; QString formatSignature(bool minimal) const; @@ -541,18 +552,25 @@ private: int m_arrayElementCount = -1; const AbstractMetaType *m_arrayElementType = nullptr; const AbstractMetaType *m_originalTemplateType = nullptr; + Indirections m_indirections; TypeUsagePattern m_pattern = InvalidPattern; uint m_constant : 1; + uint m_volatile : 1; uint m_cppInstantiation : 1; - int m_indirections : 4; - uint m_reserved : 26; // unused + uint m_reserved : 29; // unused + ReferenceType m_referenceType = NoReference; AbstractMetaTypeList m_children; Q_DISABLE_COPY(AbstractMetaType) }; +inline bool operator==(const AbstractMetaType &t1, const AbstractMetaType &t2) +{ return t1.equals(t2); } +inline bool operator!=(const AbstractMetaType &t1, const AbstractMetaType &t2) +{ return !t1.equals(t2); } + #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug d, const AbstractMetaType *at); #endif @@ -650,6 +668,13 @@ public: m_originalExpression = expr; } + bool hasDefaultValueExpression() const + { return !m_originalExpression.isEmpty() || !m_expression.isEmpty(); } + bool hasUnmodifiedDefaultValueExpression() const + { return !m_originalExpression.isEmpty() && m_originalExpression == m_expression; } + bool hasModifiedDefaultValueExpression() const + { return !m_expression.isEmpty() && m_originalExpression != m_expression; } + QString toString() const { return type()->name() + QLatin1Char(' ') + AbstractMetaVariable::name() + @@ -709,6 +734,9 @@ public: AbstractMetaField *copy() const; + static AbstractMetaField * + find(const AbstractMetaFieldList &haystack, const QString &needle); + private: mutable AbstractMetaFunction *m_getter = nullptr; mutable AbstractMetaFunction *m_setter = nullptr; @@ -816,13 +844,20 @@ public: return m_explicit; } - static bool isConversionOperator(QString funcName); + static bool isConversionOperator(const QString& funcName); + + ExceptionSpecification exceptionSpecification() const; + void setExceptionSpecification(ExceptionSpecification e); + + bool generateExceptionHandling() const { return m_generateExceptionHandling; } + void setGenerateExceptionHandling(bool g) { m_generateExceptionHandling = g; } + bool isConversionOperator() const { return isConversionOperator(originalName()); } - static bool isOperatorOverload(QString funcName); + static bool isOperatorOverload(const QString& funcName); bool isOperatorOverload() const { return isOperatorOverload(originalName()); @@ -1000,9 +1035,8 @@ public: // Returns the ownership rules for the given argument in the given context TypeSystem::Ownership ownership(const AbstractMetaClass *cls, TypeSystem::Language language, int idx) const; - bool isVirtualSlot() const; - QString typeReplaced(int argument_index) const; + bool isModifiedToArray(int argumentIndex) const; bool isRemovedFromAllLanguages(const AbstractMetaClass *) const; bool isRemovedFrom(const AbstractMetaClass *, TypeSystem::Language language) const; bool argumentRemoved(int) const; @@ -1057,11 +1091,16 @@ public: bool isCallOperator() const; + static AbstractMetaFunction * + find(const AbstractMetaFunctionList &haystack, const QString &needle); + #ifndef QT_NO_DEBUG_STREAM void formatDebugVerbose(QDebug &d) const; #endif private: + bool autoDetectAllowThread() const; + QString m_name; QString m_originalName; mutable QString m_cachedMinimalSignature; @@ -1082,6 +1121,9 @@ private: uint m_explicit : 1; uint m_pointerOperator : 1; uint m_isCallOperator : 1; + uint m_generateExceptionHandling: 1; + mutable int m_cachedAllowThread = -1; + ExceptionSpecification m_exceptionSpecification = ExceptionSpecification::Unknown; }; Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractMetaFunction::CompareResult) @@ -1246,7 +1288,8 @@ public: VirtualInCppFunctions = 0x0020000, // Only functions that are virtual in C++ VirtualInTargetLangFunctions = 0x0080000, // Only functions which are virtual in TargetLang NotRemovedFromTargetLang = 0x0400000, // Only functions that have not been removed from TargetLang - OperatorOverloads = 0x2000000 // Only functions that are operator overloads + OperatorOverloads = 0x2000000, // Only functions that are operator overloads + GenerateExceptionHandling = 0x4000000 }; Q_DECLARE_FLAGS(FunctionQueryOptions, FunctionQueryOption) Q_FLAG(FunctionQueryOption) @@ -1286,7 +1329,8 @@ public: bool hasSignal(const AbstractMetaFunction *f) const; bool hasConstructors() const; - bool hasCopyConstructor() const; + const AbstractMetaFunction *copyConstructor() const; + bool hasCopyConstructor() const { return copyConstructor() != nullptr; } bool hasPrivateCopyConstructor() const; void addDefaultConstructor(); @@ -1347,10 +1391,18 @@ public: return (hasNonPrivateConstructor() || !hasPrivateConstructor()) && !hasPrivateDestructor(); } + bool generateExceptionHandling() const; + AbstractMetaFunctionList queryFunctionsByName(const QString &name) const; + static bool queryFunction(const AbstractMetaFunction *f, FunctionQueryOptions query); + static AbstractMetaFunctionList queryFunctionList(const AbstractMetaFunctionList &list, + FunctionQueryOptions query); + static const AbstractMetaFunction *queryFirstFunction(const AbstractMetaFunctionList &list, + FunctionQueryOptions query); + AbstractMetaFunctionList queryFunctions(FunctionQueryOptions query) const; AbstractMetaFunctionList functionsInTargetLang() const; - inline AbstractMetaFunctionList cppSignalFunctions() const; + AbstractMetaFunctionList cppSignalFunctions() const; AbstractMetaFunctionList implicitConversions() const; /** @@ -1383,6 +1435,8 @@ public: m_fields << field; } + AbstractMetaField *findField(const QString &name) const; + AbstractMetaEnumList enums() const { return m_enums; @@ -1478,11 +1532,6 @@ public: m_forceShellClass = on; } - bool hasVirtualSlots() const - { - return m_hasVirtualSlots; - } - /** * Says if the class that declares or inherits a virtual function. * \return true if the class implements or inherits any virtual methods @@ -1650,6 +1699,8 @@ public: return m_hasToStringCapability; } + bool deleteInMainThread() const; + static AbstractMetaClass *findClass(const AbstractMetaClassList &classes, const QString &name); static AbstractMetaClass *findClass(const AbstractMetaClassList &classes, @@ -1666,7 +1717,6 @@ private: uint m_hasVirtuals : 1; uint m_isPolymorphic : 1; uint m_hasNonpublic : 1; - uint m_hasVirtualSlots : 1; uint m_hasNonPrivateConstructor : 1; uint m_hasPrivateConstructor : 1; uint m_functionsFixed : 1; @@ -1784,11 +1834,4 @@ private: int m_index = -1; }; -inline AbstractMetaFunctionList AbstractMetaClass::cppSignalFunctions() const -{ - return queryFunctions(Signals - | Visible - | NotRemovedFromTargetLang); -} - #endif // ABSTRACTMETALANG_H diff --git a/sources/shiboken2/ApiExtractor/apiextractor.cpp b/sources/shiboken2/ApiExtractor/apiextractor.cpp index 171011cd4..775485c81 100644 --- a/sources/shiboken2/ApiExtractor/apiextractor.cpp +++ b/sources/shiboken2/ApiExtractor/apiextractor.cpp @@ -177,41 +177,9 @@ static const AbstractMetaEnum* findEnumOnClasses(AbstractMetaClassList metaClass return result; } -const AbstractMetaEnum* ApiExtractor::findAbstractMetaEnum(const EnumTypeEntry* typeEntry) const -{ - if (!typeEntry) - return 0; - const AbstractMetaEnumList &globalEnums = m_builder->globalEnums(); - for (AbstractMetaEnum* metaEnum : globalEnums) { - if (metaEnum->typeEntry() == typeEntry) - return metaEnum; - } - return findEnumOnClasses(m_builder->classes(), typeEntry); -} - const AbstractMetaEnum* ApiExtractor::findAbstractMetaEnum(const TypeEntry* typeEntry) const { - if (!typeEntry) - return 0; - if (typeEntry->isFlags()) - return findAbstractMetaEnum(reinterpret_cast<const FlagsTypeEntry*>(typeEntry)); - if (typeEntry->isEnum()) - return findAbstractMetaEnum(reinterpret_cast<const EnumTypeEntry*>(typeEntry)); - return 0; -} - -const AbstractMetaEnum* ApiExtractor::findAbstractMetaEnum(const FlagsTypeEntry* typeEntry) const -{ - if (!typeEntry) - return 0; - return findAbstractMetaEnum(typeEntry->originator()); -} - -const AbstractMetaEnum* ApiExtractor::findAbstractMetaEnum(const AbstractMetaType* metaType) const -{ - if (!metaType) - return 0; - return findAbstractMetaEnum(metaType->typeEntry()); + return m_builder->findEnum(typeEntry); } int ApiExtractor::classCount() const diff --git a/sources/shiboken2/ApiExtractor/apiextractor.h b/sources/shiboken2/ApiExtractor/apiextractor.h index 674e5a742..ab520c9de 100644 --- a/sources/shiboken2/ApiExtractor/apiextractor.h +++ b/sources/shiboken2/ApiExtractor/apiextractor.h @@ -87,10 +87,7 @@ public: PrimitiveTypeEntryList primitiveTypes() const; ContainerTypeEntryList containerTypes() const; - const AbstractMetaEnum* findAbstractMetaEnum(const EnumTypeEntry* typeEntry) const; const AbstractMetaEnum* findAbstractMetaEnum(const TypeEntry* typeEntry) const; - const AbstractMetaEnum* findAbstractMetaEnum(const FlagsTypeEntry* typeEntry) const; - const AbstractMetaEnum* findAbstractMetaEnum(const AbstractMetaType* metaType) const; int classCount() const; diff --git a/sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp b/sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp index af7f96068..40f915028 100644 --- a/sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp +++ b/sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp @@ -146,6 +146,7 @@ class BuilderPrivate { public: typedef QHash<CXCursor, ClassModelItem> CursorClassHash; typedef QHash<CXCursor, TypeDefModelItem> CursorTypedefHash; + typedef QHash<CXType, TypeInfo> TypeInfoHash; explicit BuilderPrivate(BaseVisitor *bv) : m_baseVisitor(bv), m_model(new CodeModel) { @@ -180,9 +181,16 @@ public: CodeModel::FunctionType t = CodeModel::Normal) const; FunctionModelItem createMemberFunction(const CXCursor &cursor) const; void qualifyConstructor(const CXCursor &cursor); + TypeInfo createTypeInfoHelper(const CXType &type) const; // uncashed TypeInfo createTypeInfo(const CXType &type) const; TypeInfo createTypeInfo(const CXCursor &cursor) const { return createTypeInfo(clang_getCursorType(cursor)); } + void addTemplateInstantiations(const CXType &type, + QString *typeName, + TypeInfo *t) const; + bool addTemplateInstantiationsRecursion(const CXType &type, TypeInfo *t) const; + + void addTypeDef(const CXCursor &cursor, const TypeInfo &ti); TemplateParameterModelItem createTemplateParameter(const CXCursor &cursor) const; TemplateParameterModelItem createNonTypeTemplateParameter(const CXCursor &cursor) const; @@ -205,6 +213,8 @@ public: CursorClassHash m_cursorClassHash; CursorTypedefHash m_cursorTypedefHash; + mutable TypeInfoHash m_typeInfoHash; // Cache type information + ClassModelItem m_currentClass; EnumModelItem m_currentEnum; FunctionModelItem m_currentFunction; @@ -247,6 +257,25 @@ bool BuilderPrivate::addClass(const CXCursor &cursor, CodeModel::ClassType t) return true; } +static inline ExceptionSpecification exceptionSpecificationFromClang(int ce) +{ + switch (ce) { + case CXCursor_ExceptionSpecificationKind_BasicNoexcept: + case CXCursor_ExceptionSpecificationKind_ComputedNoexcept: + case CXCursor_ExceptionSpecificationKind_DynamicNone: // throw() + return ExceptionSpecification::NoExcept; + case CXCursor_ExceptionSpecificationKind_Dynamic: // throw(t1..) + case CXCursor_ExceptionSpecificationKind_MSAny: // throw(...) + return ExceptionSpecification::Throws; + default: + // CXCursor_ExceptionSpecificationKind_None, + // CXCursor_ExceptionSpecificationKind_Unevaluated, + // CXCursor_ExceptionSpecificationKind_Uninstantiated + break; + } + return ExceptionSpecification::Unknown; +} + FunctionModelItem BuilderPrivate::createFunction(const CXCursor &cursor, CodeModel::FunctionType t) const { @@ -256,10 +285,11 @@ FunctionModelItem BuilderPrivate::createFunction(const CXCursor &cursor, name = fixTypeName(name); FunctionModelItem result(new _FunctionModelItem(m_model, name)); setFileName(cursor, result.data()); - result->setType(createTypeInfo(clang_getCursorResultType(cursor))); + result->setType(createTypeInfoHelper(clang_getCursorResultType(cursor))); result->setFunctionType(t); result->setScope(m_scope); result->setStatic(clang_Cursor_getStorageClass(cursor) == CX_SC_Static); + result->setExceptionSpecification(exceptionSpecificationFromClang(clang_getCursorExceptionSpecificationType(cursor))); switch (clang_getCursorAvailability(cursor)) { case CXAvailability_Available: break; @@ -335,7 +365,7 @@ TemplateParameterModelItem BuilderPrivate::createTemplateParameter(const CXCurso TemplateParameterModelItem BuilderPrivate::createNonTypeTemplateParameter(const CXCursor &cursor) const { TemplateParameterModelItem result = createTemplateParameter(cursor); - result->setType(createTypeInfo(cursor)); + result->setType(createTypeInfoHelper(clang_getCursorType(cursor))); return result; } @@ -359,38 +389,6 @@ struct ArrayDimensionResult int position; }; -static ArrayDimensionResult arrayDimensions(const QString &typeName) -{ - ArrayDimensionResult result; - result.position = typeName.indexOf(QLatin1Char('[')); - for (int openingPos = result.position; openingPos != -1; ) { - const int closingPos = typeName.indexOf(QLatin1Char(']'), openingPos + 1); - if (closingPos == -1) - break; - result.dimensions.append(typeName.midRef(openingPos + 1, closingPos - openingPos - 1)); - openingPos = typeName.indexOf(QLatin1Char('['), closingPos + 1); - } - return result; -} - -// Array helpers: Parse "a[2][4]" into a list of dimensions or "" for none -static QStringList parseArrayArgs(const CXType &type, QString *typeName) -{ - const ArrayDimensionResult dimensions = arrayDimensions(*typeName); - Q_ASSERT(!dimensions.dimensions.isEmpty()); - - QStringList result; - // get first dimension from clang, preferably. - // "a[]" is seen as pointer by Clang, set special indicator "" - const long long size = clang_getArraySize(type); - result.append(size >= 0 ? QString::number(size) : QString()); - // Parse out remaining dimensions - for (int i = 1, count = dimensions.dimensions.size(); i < count; ++i) - result.append(dimensions.dimensions.at(i).toString()); - typeName->truncate(dimensions.position); - return result; -} - // Create qualified name "std::list<std::string>" -> ("std", "list<std::string>") static QStringList qualifiedName(const QString &t) { @@ -412,71 +410,143 @@ static QStringList qualifiedName(const QString &t) return result; } -TypeInfo BuilderPrivate::createTypeInfo(const CXType &type) const +static bool isArrayType(CXTypeKind k) +{ + return k == CXType_ConstantArray || k == CXType_IncompleteArray + || k == CXType_VariableArray || k == CXType_DependentSizedArray; +} + +static bool isPointerType(CXTypeKind k) +{ + return k == CXType_Pointer || k == CXType_LValueReference || k == CXType_RValueReference; +} + +bool BuilderPrivate::addTemplateInstantiationsRecursion(const CXType &type, TypeInfo *t) const +{ + // Template arguments + switch (type.kind) { + case CXType_Elaborated: + case CXType_Record: + case CXType_Unexposed: + if (const int numTemplateArguments = qMax(0, clang_Type_getNumTemplateArguments(type))) { + for (unsigned tpl = 0; tpl < unsigned(numTemplateArguments); ++tpl) { + const CXType argType = clang_Type_getTemplateArgumentAsType(type, tpl); + // CXType_Invalid is returned when hitting on a specialization + // of a non-type template (template <int v>). + if (argType.kind == CXType_Invalid) + return false; + t->addInstantiation(createTypeInfoHelper(argType)); + } + } + break; + default: + break; + } + return true; +} + +static void dummyTemplateArgumentHandler(int, const QStringRef &) {} + +void BuilderPrivate::addTemplateInstantiations(const CXType &type, + QString *typeName, + TypeInfo *t) const +{ + // In most cases, for templates like "Vector<A>", Clang will give us the + // arguments by recursing down the type. However this will fail for example + // within template classes (for functions like the copy constructor): + // template <class T> + // class Vector { + // Vector(const Vector&); + // }; + // In that case, have TypeInfo parse the list from the spelling. + // Finally, remove the list "<>" from the type name. + const bool parsed = addTemplateInstantiationsRecursion(type, t) + && !t->instantiations().isEmpty(); + const QPair<int, int> pos = parsed + ? parseTemplateArgumentList(*typeName, dummyTemplateArgumentHandler) + : t->parseTemplateArgumentList(*typeName); + if (pos.first != -1 && pos.second != -1 && pos.second > pos.first) + typeName->remove(pos.first, pos.second - pos.first); +} + +TypeInfo BuilderPrivate::createTypeInfoHelper(const CXType &type) const { if (type.kind == CXType_Pointer) { // Check for function pointers, first. const CXType pointeeType = clang_getPointeeType(type); const int argCount = clang_getNumArgTypes(pointeeType); if (argCount >= 0) { - TypeInfo result = createTypeInfo(clang_getResultType(pointeeType)); + TypeInfo result = createTypeInfoHelper(clang_getResultType(pointeeType)); result.setFunctionPointer(true); for (int a = 0; a < argCount; ++a) - result.addArgument(createTypeInfo(clang_getArgType(pointeeType, unsigned(a)))); + result.addArgument(createTypeInfoHelper(clang_getArgType(pointeeType, unsigned(a)))); return result; } } TypeInfo typeInfo; - QString typeName = fixTypeName(getTypeName(type)); - int indirections = 0; - // "int **" - for ( ; typeName.endsWith(QLatin1Char('*')) ; ++indirections) - typeName.chop(1); - typeInfo.setIndirections(indirections); - // "int &&" - if (typeName.endsWith(QLatin1String("&&"))) { - typeName.chop(2); - typeInfo.setReferenceType(RValueReference); - } else if (typeName.endsWith(QLatin1Char('&'))) { // "int &" - typeName.chop(1); - typeInfo.setReferenceType(LValueReference); + CXType nestedType = type; + for (; isArrayType(nestedType.kind); nestedType = clang_getArrayElementType(nestedType)) { + const long long size = clang_getArraySize(nestedType); + typeInfo.addArrayElement(size >= 0 ? QString::number(size) : QString()); } - // "int [3], int[]" - if (type.kind == CXType_ConstantArray || type.kind == CXType_IncompleteArray - || type.kind == CXType_VariableArray || type.kind == CXType_DependentSizedArray) { - typeInfo.setArrayElements(parseArrayArgs(type, &typeName)); + TypeInfo::Indirections indirections; + for (; isPointerType(nestedType.kind); nestedType = clang_getPointeeType(nestedType)) { + switch (nestedType.kind) { + case CXType_Pointer: + indirections.prepend(clang_isConstQualifiedType(nestedType) != 0 + ? Indirection::ConstPointer : Indirection::Pointer); + break; + case CXType_LValueReference: + typeInfo.setReferenceType(LValueReference); + break; + case CXType_RValueReference: + typeInfo.setReferenceType(RValueReference); + break; + default: + break; + } } + typeInfo.setIndirectionsV(indirections); - bool isConstant = clang_isConstQualifiedType(type) != 0; - // A "char *const" parameter, is considered to be const-qualified by Clang, but - // not in the TypeInfo sense (corresponds to "char *" and not "const char *"). - if (type.kind == CXType_Pointer && isConstant && typeName.endsWith(QLatin1String("const"))) { - typeName.chop(5); - typeName = typeName.trimmed(); - isConstant = false; - } - // Clang has been observed to return false for "const int .." - if (!isConstant && typeName.startsWith(QLatin1String("const "))) { - typeName.remove(0, 6); - isConstant = true; - } - typeInfo.setConstant(isConstant); + typeInfo.setConstant(clang_isConstQualifiedType(nestedType) != 0); + typeInfo.setVolatile(clang_isVolatileQualifiedType(nestedType) != 0); - // clang_isVolatileQualifiedType() returns true for "volatile int", but not for "volatile int *" - if (typeName.startsWith(QLatin1String("volatile "))) { - typeName.remove(0, 9); - typeInfo.setVolatile(true); + QString typeName = getTypeName(nestedType); + while (TypeInfo::stripLeadingConst(&typeName) + || TypeInfo::stripLeadingVolatile(&typeName)) { } - typeName = typeName.trimmed(); + // Obtain template instantiations if the name has '<' (thus excluding + // typedefs like "std::string". + if (typeName.contains(QLatin1Char('<'))) + addTemplateInstantiations(nestedType, &typeName, &typeInfo); typeInfo.setQualifiedName(qualifiedName(typeName)); // 3320:CINDEX_LINKAGE int clang_getNumArgTypes(CXType T); function ptr types? + typeInfo.simplifyStdType(); return typeInfo; } +TypeInfo BuilderPrivate::createTypeInfo(const CXType &type) const +{ + TypeInfoHash::iterator it = m_typeInfoHash.find(type); + if (it == m_typeInfoHash.end()) + it = m_typeInfoHash.insert(type, createTypeInfoHelper(type)); + return it.value(); +} + +void BuilderPrivate::addTypeDef(const CXCursor &cursor, const TypeInfo &ti) +{ + TypeDefModelItem item(new _TypeDefModelItem(m_model, getCursorSpelling(cursor))); + setFileName(cursor, item.data()); + item->setType(ti); + item->setScope(m_scope); + m_scopeStack.back()->addTypeDef(item); + m_cursorTypedefHash.insert(cursor, item); +} + // extract an expression from the cursor via source // CXCursor_EnumConstantDecl, ParmDecl (a = Flag1 | Flag2) QString BuilderPrivate::cursorValueExpression(BaseVisitor *bv, const CXCursor &cursor) const @@ -795,9 +865,8 @@ BaseVisitor::StartTokenResult Builder::startToken(const CXCursor &cursor) d->m_currentFunction = d->createMemberFunction(cursor); d->m_scopeStack.back()->addFunction(d->m_currentFunction); break; - } else { - return Skip; // inline member functions outside class } + return Skip; // inline member functions outside class } } Q_FALLTHROUGH(); // fall through to free template function. @@ -868,17 +937,14 @@ BaseVisitor::StartTokenResult Builder::startToken(const CXCursor &cursor) } break; case CXCursor_TypeAliasDecl: - case CXCursor_TypeAliasTemplateDecl: // May contain nested CXCursor_TemplateTypeParameter - return Skip; - case CXCursor_TypedefDecl: { - const QString name = getCursorSpelling(cursor); - TypeDefModelItem item(new _TypeDefModelItem(d->m_model, name)); - setFileName(cursor, item.data()); - item->setType(d->createTypeInfo(clang_getTypedefDeclUnderlyingType(cursor))); - item->setScope(d->m_scope); - d->m_scopeStack.back()->addTypeDef(item); - d->m_cursorTypedefHash.insert(cursor, item); + case CXCursor_TypeAliasTemplateDecl: { // May contain nested CXCursor_TemplateTypeParameter + const CXType type = clang_getCanonicalType(clang_getCursorType(cursor)); + if (type.kind > CXType_Unexposed) + d->addTypeDef(cursor, d->createTypeInfo(type)); } + return Skip; + case CXCursor_TypedefDecl: + d->addTypeDef(cursor, d->createTypeInfo(clang_getTypedefDeclUnderlyingType(cursor))); break; case CXCursor_TypeRef: if (!d->m_currentFunction.isNull()) { diff --git a/sources/shiboken2/ApiExtractor/clangparser/clangparser.cpp b/sources/shiboken2/ApiExtractor/clangparser/clangparser.cpp index ce0b6554d..e116f8b83 100644 --- a/sources/shiboken2/ApiExtractor/clangparser/clangparser.cpp +++ b/sources/shiboken2/ApiExtractor/clangparser/clangparser.cpp @@ -166,7 +166,7 @@ static inline const char **byteArrayListToFlatArgV(const QByteArrayList &bl) return result; } -static QByteArray msgCreateTranslationUnit(const QByteArrayList clangArgs, unsigned flags) +static QByteArray msgCreateTranslationUnit(const QByteArrayList &clangArgs, unsigned flags) { QByteArray result = "clang_parseTranslationUnit2(0x"; result += QByteArray::number(flags, 16); diff --git a/sources/shiboken2/ApiExtractor/clangparser/clangutils.cpp b/sources/shiboken2/ApiExtractor/clangparser/clangutils.cpp index 2ff18b23b..8bee28cdf 100644 --- a/sources/shiboken2/ApiExtractor/clangparser/clangutils.cpp +++ b/sources/shiboken2/ApiExtractor/clangparser/clangutils.cpp @@ -46,6 +46,18 @@ uint qHash(const CXCursor &c, uint seed) ^ qHash(c.data[1]) ^ qHash(c.data[2]) ^ seed; } +bool operator==(const CXType &t1, const CXType &t2) +{ + return t1.kind == t2.kind && t1.data[0] == t2.data[0] + && t1.data[1] == t2.data[1]; +} + +uint qHash(const CXType &ct, uint seed) +{ + return uint(ct.kind) ^ uint(0xFFFFFFFF & quintptr(ct.data[0])) + ^ uint(0xFFFFFFFF & quintptr(ct.data[1])) ^ seed; +} + namespace clang { SourceLocation getExpansionLocation(const CXSourceLocation &location) @@ -160,6 +172,43 @@ QVector<Diagnostic> getDiagnostics(CXTranslationUnit tu) return result; } +QPair<int, int> parseTemplateArgumentList(const QString &l, + const TemplateArgumentHandler &handler, + int from) +{ + const int ltPos = l.indexOf(QLatin1Char('<'), from); + if (ltPos == - 1) + return qMakePair(-1, -1); + int startPos = ltPos + 1; + int level = 1; + for (int p = startPos, end = l.size(); p < end; ) { + const char c = l.at(p).toLatin1(); + switch (c) { + case ',': + case '>': + handler(level, l.midRef(startPos, p - startPos).trimmed()); + ++p; + if (c == '>') { + if (--level == 0) + return qMakePair(ltPos, p); + // Skip over next ',': "a<b<c,d>,e>" + for (; p < end && (l.at(p).isSpace() || l.at(p) == QLatin1Char(',')); ++p) {} + } + startPos = p; + break; + case '<': + handler(level, l.midRef(startPos, p - startPos).trimmed()); + ++level; + startPos = ++p; + break; + default: + ++p; + break; + } + } + return qMakePair(-1, -1); +} + CXDiagnosticSeverity maxSeverity(const QVector<Diagnostic> &ds) { CXDiagnosticSeverity result = CXDiagnostic_Ignored; diff --git a/sources/shiboken2/ApiExtractor/clangparser/clangutils.h b/sources/shiboken2/ApiExtractor/clangparser/clangutils.h index 98d0c9752..db2db6267 100644 --- a/sources/shiboken2/ApiExtractor/clangparser/clangutils.h +++ b/sources/shiboken2/ApiExtractor/clangparser/clangutils.h @@ -32,13 +32,19 @@ #include <clang-c/Index.h> #include <QtCore/QPair> #include <QtCore/QString> +#include <QtCore/QStringList> #include <QtCore/QVector> +#include <functional> + QT_FORWARD_DECLARE_CLASS(QDebug) bool operator==(const CXCursor &c1, const CXCursor &c2); uint qHash(const CXCursor &c, uint seed = 0); +bool operator==(const CXType &t1, const CXType &t2); +uint qHash(const CXType &ct, uint seed); + namespace clang { QString getCursorKindName(CXCursorKind cursorKind); @@ -92,6 +98,14 @@ struct Diagnostic { QVector<Diagnostic> getDiagnostics(CXTranslationUnit tu); CXDiagnosticSeverity maxSeverity(const QVector<Diagnostic> &ds); +// Parse a template argument list "a<b<c,d>,e>" and invoke a handler +// with each match (level and string). Return begin and end of the list. +typedef std::function<void(int /*level*/, const QStringRef &)> TemplateArgumentHandler; + +QPair<int, int> parseTemplateArgumentList(const QString &l, + const TemplateArgumentHandler &handler, + int from = 0); + #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug, const SourceLocation &); QDebug operator<<(QDebug, const Diagnostic &); diff --git a/sources/shiboken2/ApiExtractor/clangparser/compilersupport.cpp b/sources/shiboken2/ApiExtractor/clangparser/compilersupport.cpp index 74cad05ae..d3d5c8da8 100644 --- a/sources/shiboken2/ApiExtractor/clangparser/compilersupport.cpp +++ b/sources/shiboken2/ApiExtractor/clangparser/compilersupport.cpp @@ -239,11 +239,11 @@ static QByteArray noStandardIncludeOption() { return QByteArrayLiteral("-nostdin #endif #if NEED_CLANG_BUILTIN_INCLUDES -static QString findClang() +static QString findClangLibDir() { for (const char *envVar : {"LLVM_INSTALL_DIR", "CLANG_INSTALL_DIR"}) { if (qEnvironmentVariableIsSet(envVar)) { - const QString path = QFile::decodeName(qgetenv(envVar)); + const QString path = QFile::decodeName(qgetenv(envVar)) + QLatin1String("/lib"); if (QFileInfo::exists(path)) return path; } @@ -252,7 +252,7 @@ static QString findClang() QStandardPaths::findExecutable(QLatin1String("llvm-config")); if (!llvmConfig.isEmpty()) { QByteArray stdOut; - if (runProcess(llvmConfig, QStringList{QLatin1String("--prefix")}, &stdOut)) { + if (runProcess(llvmConfig, QStringList{QLatin1String("--libdir")}, &stdOut)) { const QString path = QFile::decodeName(stdOut.trimmed()); if (QFileInfo::exists(path)) return path; @@ -264,11 +264,11 @@ static QString findClang() static QString findClangBuiltInIncludesDir() { // Find the include directory of the highest version. - const QString clangPath = findClang(); - if (!clangPath.isEmpty()) { + const QString clangPathLibDir = findClangLibDir(); + if (!clangPathLibDir.isEmpty()) { QString candidate; QVersionNumber lastVersionNumber(1, 0, 0); - QDir clangDir(clangPath + QLatin1String("/lib/clang")); + QDir clangDir(clangPathLibDir + QLatin1String("/clang")); const QFileInfoList versionDirs = clangDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); for (const QFileInfo &fi : versionDirs) { diff --git a/sources/shiboken2/ApiExtractor/doc/conf.py.in b/sources/shiboken2/ApiExtractor/doc/conf.py.in index 7251aaccd..185709590 100644 --- a/sources/shiboken2/ApiExtractor/doc/conf.py.in +++ b/sources/shiboken2/ApiExtractor/doc/conf.py.in @@ -43,7 +43,7 @@ source_encoding = 'utf-8' # General information about the project. project = u'API Extractor' -copyright = u'2009-2010, Nokia Corporation' +copyright = u'© 2018 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the <a href="http://www.gnu.org/license/fdl.html">GNU Free Documentation License version 1.3</a> as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -132,10 +132,6 @@ html_theme_path = ['@CMAKE_CURRENT_SOURCE_DIR@/_themes'] # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -html_use_smartypants = True - # Custom sidebar templates, maps document names to template names. #html_sidebars = { '' : ''} diff --git a/sources/shiboken2/ApiExtractor/doc/typesystem_conversionrule.rst b/sources/shiboken2/ApiExtractor/doc/typesystem_conversionrule.rst index c62d5bbf6..27e7a72de 100644 --- a/sources/shiboken2/ApiExtractor/doc/typesystem_conversionrule.rst +++ b/sources/shiboken2/ApiExtractor/doc/typesystem_conversionrule.rst @@ -63,6 +63,7 @@ native-to-target **%INTYPE_#**, should be replaced by the types used in the container template (e.g. **%INTYPE_0** correspondes to **"int"** for **"list<int>"**). + The ``file`` and ``snippet`` attributes are also supported (see :ref:`inject-code` nodes). .. _target-to-native: @@ -111,3 +112,5 @@ add-conversion **%in**, **%out**, **%INTYPE**, **%INTYPE_#**, and **%OUTTYPE**, must be provided by the generator as in the ``native-to-target`` tag. + The ``file`` and ``snippet`` attributes are also supported (see :ref:`inject-code` nodes). + diff --git a/sources/shiboken2/ApiExtractor/doc/typesystem_documentation.rst b/sources/shiboken2/ApiExtractor/doc/typesystem_documentation.rst index 43f72a1ba..d73e43cb1 100644 --- a/sources/shiboken2/ApiExtractor/doc/typesystem_documentation.rst +++ b/sources/shiboken2/ApiExtractor/doc/typesystem_documentation.rst @@ -30,9 +30,9 @@ modify-documentation ^^^^^^^^^^^^^^^^^^^^ The modify-documentation node allows you to change the auto-generated - documentation. API Extractor transforms XML's from qdoc3 (the Qt documentation - tool) into .rst files to be processed later using Sphinx. So you can modify - the XML before the transformation occur. + documentation. API Extractor transforms XML's from qdoc (the Qt documentation + tool) into .rst files to be processed later using Sphinx. You can modify + the XML before the transformation takes place. .. code-block:: xml diff --git a/sources/shiboken2/ApiExtractor/doc/typesystem_manipulating_objects.rst b/sources/shiboken2/ApiExtractor/doc/typesystem_manipulating_objects.rst index ff6ea5317..12b866ad7 100644 --- a/sources/shiboken2/ApiExtractor/doc/typesystem_manipulating_objects.rst +++ b/sources/shiboken2/ApiExtractor/doc/typesystem_manipulating_objects.rst @@ -11,6 +11,9 @@ inject-code given type or function, and it is a child of the :ref:`object-type`, :ref:`value-type`, :ref:`modify-function` and :ref:`add-function` nodes. + The code can be embedded into XML (be careful to use the correct XML entities + for characters like '<', '>', '&'): + .. code-block:: xml <value-type> @@ -20,6 +23,18 @@ inject-code </inject-code> </value-type> + or obtained from an external file: + + .. code-block:: xml + + <value-type> + <inject-code class="native | target | target-declaration" + position="beginning | end" since="..." + file="external_source.cpp" + snippet="label"/> + </value-type> + + The ``class`` attribute specifies which module of the generated code that will be affected by the code injection. The ``class`` attribute accepts the following values: @@ -28,6 +43,8 @@ inject-code * target: The binding code * target-declaration: The code will be injected into the generated header file containing the c++ wrapper class definition. + * file: The file name + * snippet: The snippet label (optional) If the ``position`` attribute is set to *beginning* (the default), the code is inserted at the beginning of the function. If it is set to *end*, the code @@ -35,6 +52,16 @@ inject-code The ``since`` attribute specify the API version where this code was injected. + If a ``snippet`` label is given, the code between annotations of the form + + .. code-block:: c++ + + // @snippet label + ... + // @snippet label + + will be extracted. + modify-field ^^^^^^^^^^^^ @@ -74,6 +101,8 @@ modify-function since="..." remove="all | c++" access="public | private | protected" + allow-thread="true | auto | false" + exception-handling="off | auto-off | auto-on | on" rename="..." /> </object-type> @@ -82,6 +111,26 @@ modify-function The ``since`` attribute specify the API version when this function was modified. + The ``allow-thread`` attribute specifies whether a function should be wrapped + into ``Py_BEGIN_ALLOW_THREADS`` and ``Py_END_ALLOW_THREADS``, that is, + temporarily release the GIL (global interpreter lock). Doing so is required + for any thread-related function (wait operations), functions that might call + a virtual function (potentially reimplemented in Python), and recommended for + lengthy I/O operations or similar. It has performance costs, though. + The value ``auto`` means that it will be turned off for functions for which + it is deemed to be safe, for example, simple getters. + + The ``exception-handling`` attribute specifies whether to generate exception + handling code (nest the function call into try / catch statements). It accepts + the following values: + + * no, false: Do not generate exception handling code + * auto-off: Generate exception handling code for functions + declaring a non-empty ``throw`` list + * auto-on: Generate exception handling code unless function + declares ``noexcept`` + * yes, true: Always generate exception handling code + The ``remove``, ``access`` and ``rename`` attributes are *optional* attributes for added convenience; they serve the same purpose as the deprecated tags :ref:`remove`, :ref:`access` and :ref:`rename`. @@ -130,3 +179,4 @@ conversion-rule .. note:: You can also use the conversion-rule node to specify :ref:`how the conversion of a single function argument should be done in a function <conversion-rule>`. + The ``file`` and ``snippet`` attributes are also supported (see :ref:`inject-code` nodes). diff --git a/sources/shiboken2/ApiExtractor/doc/typesystem_specifying_types.rst b/sources/shiboken2/ApiExtractor/doc/typesystem_specifying_types.rst index 322f9bca6..c3180ae88 100644 --- a/sources/shiboken2/ApiExtractor/doc/typesystem_specifying_types.rst +++ b/sources/shiboken2/ApiExtractor/doc/typesystem_specifying_types.rst @@ -11,7 +11,7 @@ typesystem .. code-block:: xml - <typesystem package="..." default-superclass="..."> + <typesystem package="..." default-superclass="..." exception-handling="..."> </typesystem> The **package** attribute is a string describing the package to be used, @@ -19,6 +19,9 @@ typesystem The *optional* **default-superclass** attribute is the canonical C++ base class name of all objects, e.g., "object". + The *optional* **exception-handling** attribute specifies the default exception + handling mode of all objects (see :ref:`modify-function`). + load-typesystem ^^^^^^^^^^^^^^^ @@ -216,6 +219,7 @@ value-type <typesystem> <value-type name="..." since="..." copyable="yes | no" + exception-handling="..." hash-function="..." stream="yes | no" default-constructor="..." @@ -243,6 +247,9 @@ value-type The **revision** attribute can be used to specify a revision for each type, easing the production of ABI compatible bindings. + The *optional* **exception-handling** attribute specifies the default exception + handling mode of all functions (see :ref:`modify-function`). + .. _object-type: object-type @@ -258,6 +265,7 @@ object-type <object-type name="..." since="..." copyable="yes | no" + exception-handling="..." hash-function="..." stream="yes | no" revision="..." /> @@ -278,6 +286,9 @@ object-type The **revision** attribute can be used to specify a revision for each type, easing the production of ABI compatible bindings. + The *optional* **exception-handling** attribute specifies the default exception + handling mode of all functions (see :ref:`modify-function`). + interface-type ^^^^^^^^^^^^^^ @@ -329,6 +340,38 @@ container-type The *optional* **since** value is used to specify the API version of this container. +typedef-type +^^^^^^^^^^^^ + + The typedef-type allows for specifying typedefs in the typesystem. They + are mostly equivalent to spelling out the typedef in the included header, which + is often complicated when trying to wrap libraries whose source code cannot be + easily extended. + + .. code-block:: xml + + <typesystem> + <typedef-type name="..." + source="..." + since="..." + </typesystem> + + The **source** attribute is the source. Example: + + .. code-block:: xml + + <namespace-type name='std'> + <value-type name='optional' generate='no'/>\n" + </namespace-type> + <typedef-type name="IntOptional" source="std::optional<int>"/> + + is equivalent to + + .. code-block:: c++ + + typedef std::optional<int> IntOptional; + + The *optional* **since** value is used to specify the API version of this type. .. _custom-type: @@ -348,6 +391,26 @@ custom-type The **name** attribute is the name of the custom type, e.g., "PyObject". +.. _smart-pointer-type: + +smart-pointer-type +^^^^^^^^^^^^^^^^^^ + + The smart pointer type node indicates that the given class is a smart pointer + and requires inserting calls to **getter** to access the pointeee. + Currently, only the **type** *shared* is supported and the usage is limited + to function return values. + **ref-count-method** specifies the name of the method used to do reference counting. + + .. code-block:: xml + + <typesystem> + <smart-pointer-type name="..." + since="..." + type="..." + getter="..." + ref-count-method="..."/> + </typesystem> .. _function: diff --git a/sources/shiboken2/ApiExtractor/docparser.cpp b/sources/shiboken2/ApiExtractor/docparser.cpp index 9305332ba..99921e7d3 100644 --- a/sources/shiboken2/ApiExtractor/docparser.cpp +++ b/sources/shiboken2/ApiExtractor/docparser.cpp @@ -27,6 +27,7 @@ ****************************************************************************/ #include "docparser.h" #include "abstractmetalang.h" +#include "messages.h" #include "reporthandler.h" #include "typesystem.h" #include <QtCore/QDebug> @@ -50,9 +51,7 @@ DocParser::DocParser() #endif } -DocParser::~DocParser() -{ -} +DocParser::~DocParser() = default; QString DocParser::getDocumentation(QXmlQuery& xquery, const QString& query, const DocModificationList& mods) const @@ -109,58 +108,21 @@ AbstractMetaFunctionList DocParser::documentableFunctions(const AbstractMetaClas return result; } -QString DocParser::msgCannotFindDocumentation(const QString &fileName, - const char *what, const QString &name, - const QString &query) -{ - QString result; - QTextStream(&result) << "Cannot find documentation for " << what - << ' ' << name << " in:\n " << QDir::toNativeSeparators(fileName) - << "\n using query:\n " << query; - return result; -} - -QString DocParser::msgCannotFindDocumentation(const QString &fileName, - const AbstractMetaClass *metaClass, - const AbstractMetaFunction *function, - const QString &query) -{ - const QString name = metaClass->name() + QLatin1String("::") - + function->minimalSignature(); - return msgCannotFindDocumentation(fileName, "function", name, query); -} - -QString DocParser::msgCannotFindDocumentation(const QString &fileName, - const AbstractMetaClass *metaClass, - const AbstractMetaEnum *e, - const QString &query) -{ - return msgCannotFindDocumentation(fileName, "enum", - metaClass->name() + QLatin1String("::") + e->name(), - query); -} - -QString DocParser::msgCannotFindDocumentation(const QString &fileName, - const AbstractMetaClass *metaClass, - const AbstractMetaField *f, - const QString &query) -{ - return msgCannotFindDocumentation(fileName, "field", - metaClass->name() + QLatin1String("::") + f->name(), - query); -} #ifdef HAVE_LIBXSLT namespace { -struct XslResources +class XslResources { - xmlDocPtr xmlDoc; - xsltStylesheetPtr xslt; - xmlDocPtr xslResult; + Q_DISABLE_COPY(XslResources) + +public: + xmlDocPtr xmlDoc = nullptr; + xsltStylesheetPtr xslt = nullptr; + xmlDocPtr xslResult = nullptr; - XslResources() : xmlDoc(0), xslt(0), xslResult(0) {} + XslResources() = default; ~XslResources() { @@ -186,27 +148,6 @@ static inline bool isXpathDocModification(const DocModification &mod) return mod.mode() == TypeSystem::DocModificationXPathReplace; } -QString msgXpathDocModificationError(const DocModificationList& mods, - const QString &what) -{ - QString result; - QTextStream str(&result); - str << "Error when applying modifications ("; - for (const DocModification &mod : mods) { - if (isXpathDocModification(mod)) { - str << '"' << mod.xpath() << "\" -> \""; - const QString simplified = mod.code().simplified(); - if (simplified.size() > 20) - str << simplified.leftRef(20) << "..."; - else - str << simplified; - str << '"'; - } - } - str << "): " << what; - return result; -} - QString DocParser::applyDocModifications(const DocModificationList& mods, const QString& xml) const { if (mods.isEmpty() || xml.isEmpty() diff --git a/sources/shiboken2/ApiExtractor/docparser.h b/sources/shiboken2/ApiExtractor/docparser.h index fff71a877..7cbbef28e 100644 --- a/sources/shiboken2/ApiExtractor/docparser.h +++ b/sources/shiboken2/ApiExtractor/docparser.h @@ -120,22 +120,6 @@ protected: static AbstractMetaFunctionList documentableFunctions(const AbstractMetaClass *metaClass); - static QString msgCannotFindDocumentation(const QString &fileName, - const char *what, const QString &name, - const QString &query); - static QString msgCannotFindDocumentation(const QString &fileName, - const AbstractMetaClass *metaClass, - const AbstractMetaFunction *function, - const QString &query); - static QString msgCannotFindDocumentation(const QString &fileName, - const AbstractMetaClass *metaClass, - const AbstractMetaEnum *e, - const QString &query); - static QString msgCannotFindDocumentation(const QString &fileName, - const AbstractMetaClass *metaClass, - const AbstractMetaField *f, - const QString &query); - private: QString m_packageName; QString m_docDataDir; diff --git a/sources/shiboken2/ApiExtractor/doxygenparser.cpp b/sources/shiboken2/ApiExtractor/doxygenparser.cpp index dfdb37a47..e238aa1f7 100644 --- a/sources/shiboken2/ApiExtractor/doxygenparser.cpp +++ b/sources/shiboken2/ApiExtractor/doxygenparser.cpp @@ -28,6 +28,7 @@ #include "doxygenparser.h" #include "abstractmetalang.h" +#include "messages.h" #include "reporthandler.h" #include "typesystem.h" @@ -35,23 +36,17 @@ #include <QtCore/QFile> #include <QtCore/QDir> -namespace +static QString getSectionKindAttr(const AbstractMetaFunction *func) { - -QString getSectionKindAttr(const AbstractMetaFunction* func) -{ - if (func->isSignal()) { + if (func->isSignal()) return QLatin1String("signal"); - } else { - QString kind = func->isPublic() ? QLatin1String("public") : QLatin1String("protected"); - if (func->isStatic()) - kind += QLatin1String("-static"); - else if (func->isSlot()) - kind += QLatin1String("-slot"); - return kind; - } -} - + QString kind = func->isPublic() + ? QLatin1String("public") : QLatin1String("protected"); + if (func->isStatic()) + kind += QLatin1String("-static"); + else if (func->isSlot()) + kind += QLatin1String("-slot"); + return kind; } Documentation DoxygenParser::retrieveModuleDocumentation() @@ -73,13 +68,12 @@ void DoxygenParser::fillDocumentation(AbstractMetaClass* metaClass) doxyFileSuffix += QLatin1String(".xml"); const char* prefixes[] = { "class", "struct", "namespace" }; - const int numPrefixes = sizeof(prefixes) / sizeof(const char*); bool isProperty = false; QString doxyFilePath; - for (int i = 0; i < numPrefixes; ++i) { + for (const char *prefix : prefixes) { doxyFilePath = documentationDataDirectory() + QLatin1Char('/') - + QLatin1String(prefixes[i]) + doxyFileSuffix; + + QLatin1String(prefix) + doxyFileSuffix; if (QFile::exists(doxyFilePath)) break; doxyFilePath.clear(); diff --git a/sources/shiboken2/ApiExtractor/fileout.cpp b/sources/shiboken2/ApiExtractor/fileout.cpp index e3c96d57b..10a8f6be8 100644 --- a/sources/shiboken2/ApiExtractor/fileout.cpp +++ b/sources/shiboken2/ApiExtractor/fileout.cpp @@ -27,11 +27,13 @@ ****************************************************************************/ #include "fileout.h" +#include "messages.h" #include "reporthandler.h" #include <QtCore/QTextCodec> #include <QtCore/QFileInfo> #include <QtCore/QDir> +#include <QtCore/QDebug> #include <cstdio> @@ -50,24 +52,25 @@ static const char colorInfo[] = ""; static const char colorReset[] = ""; #endif -FileOut::FileOut(QString n): - name(n), - stream(&tmp), - isDone(false) -{} +FileOut::FileOut(QString n) : + name(std::move(n)), + stream(&tmp), + isDone(false) +{ +} + +FileOut::~FileOut() +{ + if (!isDone) + done(); +} -static int* lcsLength(QList<QByteArray> a, QList<QByteArray> b) +static QVector<int> lcsLength(const QByteArrayList &a, const QByteArrayList &b) { const int height = a.size() + 1; const int width = b.size() + 1; - int *res = new int[width * height]; - - for (int row = 0; row < height; row++) - res[width * row] = 0; - - for (int col = 0; col < width; col++) - res[col] = 0; + QVector<int> res(width * height, 0); for (int row = 1; row < height; row++) { for (int col = 1; col < width; col++) { @@ -89,88 +92,84 @@ enum Type { struct Unit { - Unit(Type type, int pos) : - type(type), - start(pos), - end(pos) {} - Type type; int start; int end; - void print(QList<QByteArray> a, QList<QByteArray> b) - { - if (type == Unchanged) { - if ((end - start) > 9) { - for (int i = start; i <= start + 2; i++) - std::printf(" %s\n", a[i].data()); - std::printf("%s=\n= %d more lines\n=%s\n", colorInfo, end - start - 6, colorReset); - for (int i = end - 2; i <= end; i++) - std::printf(" %s\n", a[i].data()); - } else { - for (int i = start; i <= end; i++) - std::printf(" %s\n", a[i].data()); - } - } else if (type == Add) { - std::printf("%s", colorAdd); - for (int i = start; i <= end; i++) - std::printf("+ %s\n", b[i].data()); - std::printf("%s", colorReset); - } else if (type == Delete) { - std::printf("%s", colorDelete); - for (int i = start; i <= end; i++) - std::printf("- %s\n", a[i].data()); - std::printf("%s", colorReset); - } - } + void print(const QByteArrayList &a, const QByteArrayList &b) const; }; -static QList<Unit*> *unitAppend(QList<Unit*> *res, Type type, int pos) +void Unit::print(const QByteArrayList &a, const QByteArrayList &b) const { - if (!res) { - res = new QList<Unit*>; - res->append(new Unit(type, pos)); - return res; + switch (type) { + case Unchanged: + if ((end - start) > 9) { + for (int i = start; i <= start + 2; i++) + std::printf(" %s\n", a.at(i).constData()); + std::printf("%s=\n= %d more lines\n=%s\n", + colorInfo, end - start - 6, colorReset); + for (int i = end - 2; i <= end; i++) + std::printf(" %s\n", a.at(i).constData()); + } else { + for (int i = start; i <= end; i++) + std::printf(" %s\n", a.at(i).constData()); + } + break; + case Add: + std::fputs(colorAdd, stdout); + for (int i = start; i <= end; i++) + std::printf("+ %s\n", b.at(i).constData()); + std::fputs(colorReset, stdout); + break; + case Delete: + std::fputs(colorDelete, stdout); + for (int i = start; i <= end; i++) + std::printf("- %s\n", a.at(i).constData()); + std::fputs(colorReset, stdout); + break; } +} - Unit *last = res->last(); - if (last->type == type) - last->end = pos; +static void unitAppend(Type type, int pos, QVector<Unit> *units) +{ + if (!units->isEmpty() && units->last().type == type) + units->last().end = pos; else - res->append(new Unit(type, pos)); - - return res; + units->append(Unit{type, pos, pos}); } -static QList<Unit*> *diffHelper(int *lcs, QList<QByteArray> a, QList<QByteArray> b, int row, int col) +static QVector<Unit> diffHelper(const QVector<int> &lcs, + const QByteArrayList &a, const QByteArrayList &b, + int row, int col) { - if (row > 0 && col > 0 && (a[row-1] == b[col-1])) { - return unitAppend(diffHelper(lcs, a, b, row - 1, col - 1), Unchanged, row - 1); - } else { - int width = b.size() + 1; - if ((col > 0) - && (row == 0 || lcs[width * row + col-1] >= lcs[width *(row-1) + col])) { - return unitAppend(diffHelper(lcs, a, b, row, col - 1), Add, col - 1); - } else if ((row > 0) - && (col == 0 || lcs[width * row + col-1] < lcs[width *(row-1) + col])) { - return unitAppend(diffHelper(lcs, a, b, row - 1, col), Delete, row - 1); - } + if (row > 0 && col > 0 && a.at(row - 1) == b.at(col - 1)) { + QVector<Unit> result = diffHelper(lcs, a, b, row - 1, col - 1); + unitAppend(Unchanged, row - 1, &result); + return result; } - delete lcs; - return 0; -} -static void diff(QList<QByteArray> a, QList<QByteArray> b) -{ - QList<Unit*> *res = diffHelper(lcsLength(a, b), a, b, a.size(), b.size()); - for (int i = 0; i < res->size(); i++) { - Unit *unit = res->at(i); - unit->print(a, b); - delete(unit); + const int width = b.size() + 1; + if (col > 0 + && (row == 0 || lcs.at(width * row + col -1 ) >= lcs.at(width * (row - 1) + col))) { + QVector<Unit> result = diffHelper(lcs, a, b, row, col - 1); + unitAppend(Add, col - 1, &result); + return result; } - delete(res); + if (row > 0 + && (col == 0 || lcs.at(width * row + col-1) < lcs.at(width * (row - 1) + col))) { + QVector<Unit> result = diffHelper(lcs, a, b, row - 1, col); + unitAppend(Delete, row - 1, &result); + return result; + } + return QVector<Unit>{}; } +static void diff(const QByteArrayList &a, const QByteArrayList &b) +{ + const QVector<Unit> res = diffHelper(lcsLength(a, b), a, b, a.size(), b.size()); + for (const Unit &unit : res) + unit.print(a, b); +} FileOut::State FileOut::done() { @@ -181,18 +180,6 @@ FileOut::State FileOut::done() return result; } -QString FileOut::msgCannotOpenForReading(const QFile &f) -{ - return QStringLiteral("Failed to open file '%1' for reading: %2") - .arg(QDir::toNativeSeparators(f.fileName()), f.errorString()); -} - -QString FileOut::msgCannotOpenForWriting(const QFile &f) -{ - return QStringLiteral("Failed to open file '%1' for writing: %2") - .arg(QDir::toNativeSeparators(f.fileName()), f.errorString()); -} - FileOut::State FileOut::done(QString *errorMessage) { Q_ASSERT(!isDone); @@ -245,3 +232,18 @@ FileOut::State FileOut::done(QString *errorMessage) return Success; } + +void FileOut::touchFile(const QString &filePath) +{ + QFile toucher(filePath); + qint64 size = toucher.size(); + if (!toucher.open(QIODevice::ReadWrite)) { + qCWarning(lcShiboken).noquote().nospace() + << QStringLiteral("Failed to touch file '%1'") + .arg(QDir::toNativeSeparators(filePath)); + return; + } + toucher.resize(size+1); + toucher.resize(size); + toucher.close(); +} diff --git a/sources/shiboken2/ApiExtractor/fileout.h b/sources/shiboken2/ApiExtractor/fileout.h index 539ae7a43..aace70131 100644 --- a/sources/shiboken2/ApiExtractor/fileout.h +++ b/sources/shiboken2/ApiExtractor/fileout.h @@ -43,18 +43,17 @@ private: public: enum State { Failure, Unchanged, Success }; - FileOut(QString name); - ~FileOut() - { - if (!isDone) - done(); - } + explicit FileOut(QString name); + ~FileOut(); + + QString filePath() const { return name; } State done(); State done(QString *errorMessage); - static QString msgCannotOpenForReading(const QFile &f); - static QString msgCannotOpenForWriting(const QFile &f); + void touch() { touchFile(name); } + + static void touchFile(const QString &filePath); QTextStream stream; diff --git a/sources/shiboken2/ApiExtractor/include.cpp b/sources/shiboken2/ApiExtractor/include.cpp index 963999b9d..d6a451992 100644 --- a/sources/shiboken2/ApiExtractor/include.cpp +++ b/sources/shiboken2/ApiExtractor/include.cpp @@ -36,10 +36,9 @@ QString Include::toString() const { if (m_type == IncludePath) return QLatin1String("#include <") + m_name + QLatin1Char('>'); - else if (m_type == LocalPath) + if (m_type == LocalPath) return QLatin1String("#include \"") + m_name + QLatin1Char('"'); - else - return QLatin1String("import ") + m_name + QLatin1Char(';'); + return QLatin1String("import ") + m_name + QLatin1Char(';'); } uint qHash(const Include& inc) diff --git a/sources/shiboken2/ApiExtractor/include.h b/sources/shiboken2/ApiExtractor/include.h index 16059876a..4890eea2c 100644 --- a/sources/shiboken2/ApiExtractor/include.h +++ b/sources/shiboken2/ApiExtractor/include.h @@ -42,7 +42,8 @@ public: enum IncludeType { IncludePath, LocalPath, - TargetLangImport + TargetLangImport, + InvalidInclude }; Include() : m_type(IncludePath) {} diff --git a/sources/shiboken2/ApiExtractor/messages.cpp b/sources/shiboken2/ApiExtractor/messages.cpp new file mode 100644 index 000000000..fa4c75743 --- /dev/null +++ b/sources/shiboken2/ApiExtractor/messages.cpp @@ -0,0 +1,446 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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 "messages.h" +#include "abstractmetalang.h" +#include "typesystem.h" +#include <codemodel.h> + +#include <QtCore/QCoreApplication> +#include <QtCore/QDebug> +#include <QtCore/QDir> +#include <QtCore/QFile> +#include <QtCore/QStringList> +#include <QtCore/QXmlStreamReader> + +static inline QString colonColon() { return QStringLiteral("::"); } + +// abstractmetabuilder.cpp + +QString msgNoFunctionForModification(const QString &signature, + const QString &originalSignature, + const QString &className, + const QStringList &possibleSignatures, + const AbstractMetaFunctionList &allFunctions) +{ + QString result; + QTextStream str(&result); + str << "signature '" << signature << '\''; + if (!originalSignature.isEmpty() && originalSignature != signature) + str << " (specified as '" << originalSignature << "')"; + str << " for function modification in '" + << className << "' not found."; + if (!possibleSignatures.isEmpty()) { + str << "\n Possible candidates:\n"; + for (const auto &s : possibleSignatures) + str << " " << s << '\n'; + } else if (!allFunctions.isEmpty()) { + str << "\n No candidates were found. Member functions:\n"; + const int maxCount = qMin(10, allFunctions.size()); + for (int f = 0; f < maxCount; ++f) + str << " " << allFunctions.at(f)->minimalSignature() << '\n'; + if (maxCount < allFunctions.size()) + str << " ...\n"; + } + return result; +} + +template <class Stream> +static void msgFormatEnumType(Stream &str, + const EnumModelItem &enumItem, + const QString &className) +{ + switch (enumItem->enumKind()) { + case CEnum: + str << "Enum '" << enumItem->qualifiedName().join(colonColon()) << '\''; + break; + case AnonymousEnum: { + const EnumeratorList &values = enumItem->enumerators(); + str << "Anonymous enum ("; + switch (values.size()) { + case 0: + break; + case 1: + str << values.constFirst()->name(); + break; + case 2: + str << values.at(0)->name() << ", " << values.at(1)->name(); + break; + default: + str << values.at(0)->name() << ", ... , " + << values.at(values.size() - 1)->name(); + break; + } + str << ')'; + } + break; + case EnumClass: + str << "Scoped enum '" << enumItem->qualifiedName().join(colonColon()) << '\''; + break; + } + if (!className.isEmpty()) + str << " (class: " << className << ')'; +} + +QString msgNoEnumTypeEntry(const EnumModelItem &enumItem, + const QString &className) +{ + QString result; + QTextStream str(&result); + msgFormatEnumType(str, enumItem, className); + str << " does not have a type entry"; + return result; +} + +QString msgNoEnumTypeConflict(const EnumModelItem &enumItem, + const QString &className, + const TypeEntry *t) +{ + QString result; + QDebug debug(&result); // Use the debug operator for TypeEntry::Type + debug.noquote(); + debug.nospace(); + msgFormatEnumType(debug, enumItem, className); + debug << " is not an enum (type: " << t->type() << ')'; + return result; +} + +QString msgUnmatchedParameterType(const ArgumentModelItem &arg, int n, + const QString &why) +{ + QString result; + QTextStream str(&result); + str << "unmatched type '" << arg->type().toString() << "' in parameter #" + << (n + 1); + if (!arg->name().isEmpty()) + str << " \"" << arg->name() << '"'; + str << ": " << why; + return result; +} + +QString msgUnmatchedReturnType(const FunctionModelItem &functionItem, + const QString &why) +{ + return QLatin1String("unmatched return type '") + + functionItem->type().toString() + + QLatin1String("': ") + why; +} + +QString msgSkippingFunction(const FunctionModelItem &functionItem, + const QString &signature, const QString &why) +{ + QString result; + QTextStream str(&result); + str << "skipping "; + if (functionItem->isAbstract()) + str << "abstract "; + str << "function '" << signature << "', " << why; + if (functionItem->isAbstract()) { + str << "\nThis will lead to compilation errors due to not " + "being able to instantiate the wrapper."; + } + return result; +} + +QString msgCannotSetArrayUsage(const QString &function, int i, const QString &reason) +{ + return function + QLatin1String(": Cannot use parameter ") + + QString::number(i + 1) + QLatin1String(" as an array: ") + reason; +} + +QString msgUnableToTranslateType(const QString &t, const QString &why) +{ + return QLatin1String("Unable to translate type \"") + + t + QLatin1String("\": ") + why; +} + +QString msgUnableToTranslateType(const TypeInfo &typeInfo, + const QString &why) +{ + return msgUnableToTranslateType(typeInfo.toString(), why); +} + +QString msgCannotFindTypeEntry(const QString &t) +{ + return QLatin1String("Cannot find type entry for \"") + t + QLatin1String("\"."); +} + +QString msgCannotTranslateTemplateArgument(int i, + const TypeInfo &typeInfo, + const QString &why) +{ + QString result; + QTextStream(&result) << "Unable to translate template argument " + << (i + 1) << typeInfo.toString() << ": " << why; + return result; +} + +// abstractmetalang.cpp + +QString msgDisallowThread(const AbstractMetaFunction *f) +{ + QString result; + QTextStream str(&result); + str <<"Disallowing threads for "; + if (auto c = f->declaringClass()) + str << c->name() << "::"; + str << f->name() << "()."; + return result; +} + +// docparser.cpp + +QString msgCannotFindDocumentation(const QString &fileName, + const char *what, const QString &name, + const QString &query) +{ + QString result; + QTextStream(&result) << "Cannot find documentation for " << what + << ' ' << name << " in:\n " << QDir::toNativeSeparators(fileName) + << "\n using query:\n " << query; + return result; +} + +QString msgCannotFindDocumentation(const QString &fileName, + const AbstractMetaClass *metaClass, + const AbstractMetaFunction *function, + const QString &query) +{ + const QString name = metaClass->name() + QLatin1String("::") + + function->minimalSignature(); + return msgCannotFindDocumentation(fileName, "function", name, query); +} + +QString msgCannotFindDocumentation(const QString &fileName, + const AbstractMetaClass *metaClass, + const AbstractMetaEnum *e, + const QString &query) +{ + return msgCannotFindDocumentation(fileName, "enum", + metaClass->name() + QLatin1String("::") + e->name(), + query); +} + +QString msgCannotFindDocumentation(const QString &fileName, + const AbstractMetaClass *metaClass, + const AbstractMetaField *f, + const QString &query) +{ + return msgCannotFindDocumentation(fileName, "field", + metaClass->name() + QLatin1String("::") + f->name(), + query); +} + +QString msgXpathDocModificationError(const DocModificationList& mods, + const QString &what) +{ + QString result; + QTextStream str(&result); + str << "Error when applying modifications ("; + for (const DocModification &mod : mods) { + if (mod.mode() == TypeSystem::DocModificationXPathReplace) { + str << '"' << mod.xpath() << "\" -> \""; + const QString simplified = mod.code().simplified(); + if (simplified.size() > 20) + str << simplified.leftRef(20) << "..."; + else + str << simplified; + str << '"'; + } + } + str << "): " << what; + return result; +} + +// fileout.cpp + +QString msgCannotOpenForReading(const QFile &f) +{ + return QStringLiteral("Failed to open file '%1' for reading: %2") + .arg(QDir::toNativeSeparators(f.fileName()), f.errorString()); +} + +QString msgCannotOpenForWriting(const QFile &f) +{ + return QStringLiteral("Failed to open file '%1' for writing: %2") + .arg(QDir::toNativeSeparators(f.fileName()), f.errorString()); +} + +// generator.cpp + +QString msgCannotUseEnumAsInt(const QString &name) +{ + return QLatin1String("Cannot convert the protected scoped enum \"") + name + + QLatin1String("\" to type int when generating wrappers for the protected hack. " + "Compilation errors may occur when used as a function argument."); +} + +// main.cpp + +QString msgLeftOverArguments(const QMap<QString, QString> &remainingArgs) +{ + QString message; + QTextStream str(&message); + str << "shiboken: Called with wrong arguments:"; + for (auto it = remainingArgs.cbegin(), end = remainingArgs.cend(); it != end; ++it) { + str << ' ' << it.key(); + if (!it.value().isEmpty()) + str << ' ' << it.value(); + } + str << "\nCommand line: " << QCoreApplication::arguments().join(QLatin1Char(' ')); + return message; +} + +QString msgInvalidVersion(const QString &package, const QString &version) +{ + return QLatin1String("Invalid version \"") + version + + QLatin1String("\" specified for package ") + package + QLatin1Char('.'); +} + +QString msgCyclicDependency(const QString &funcName, const QString &graphName, + const QVector<const AbstractMetaFunction *> &involvedConversions) +{ + QString result; + QTextStream str(&result); + str << "Cyclic dependency found on overloaddata for \"" << funcName + << "\" method! The graph boy saved the graph at \"" + << QDir::toNativeSeparators(graphName) << "\"."; + if (const int count = involvedConversions.size()) { + str << " Implicit conversions (" << count << "): "; + for (int i = 0; i < count; ++i) { + if (i) + str << ", \""; + str << involvedConversions.at(i)->signature() << '"'; + if (const AbstractMetaClass *c = involvedConversions.at(i)->implementingClass()) + str << '(' << c->name() << ')'; + } + } + return result; +} + +// shibokengenerator.cpp + +QString msgUnknownOperator(const AbstractMetaFunction* func) +{ + QString result = QLatin1String("Unknown operator: \"") + func->originalName() + + QLatin1Char('"'); + if (const AbstractMetaClass *c = func->implementingClass()) + result += QLatin1String(" in class: ") + c->name(); + return result; +} + +QString msgWrongIndex(const char *varName, const QString &capture, + const AbstractMetaFunction *func) +{ + QString result; + QTextStream str(&result); + str << "Wrong index for " << varName << " variable (" << capture << ") on "; + if (const AbstractMetaClass *c = func->implementingClass()) + str << c->name() << "::"; + str << func->signature(); + return result; +} + +QString msgCannotFindType(const QString &type, const QString &variable, + const QString &why) +{ + QString result; + QTextStream(&result) << "Could not find type '" + << type << "' for use in '" << variable << "' conversion: " << why + << "\nMake sure to use the full C++ name, e.g. 'Namespace::Class'."; + return result; +} + +QString msgCannotBuildMetaType(const QString &s) +{ + return QLatin1String("Unable to build meta type for \"") + + s + QLatin1String("\": "); +} + +QString msgCouldNotFindMinimalConstructor(const QString &where, const QString &type) +{ + return where + QLatin1String(": Could not find a minimal constructor for type '") + + type + QLatin1String("'. This will result in a compilation error."); +} + +// typedatabase.cpp + +QString msgRejectReason(const TypeRejection &r, const QString &needle) +{ + QString result; + QTextStream str(&result); + switch (r.matchType) { + case TypeRejection::ExcludeClass: + str << " matches class exclusion \"" << r.className.pattern() << '"'; + break; + case TypeRejection::Function: + case TypeRejection::Field: + case TypeRejection::Enum: + str << " matches class \"" << r.className.pattern() << "\" and \"" + << r.pattern.pattern() << '"'; + break; + case TypeRejection::ArgumentType: + case TypeRejection::ReturnType: + str << " matches class \"" << r.className.pattern() << "\" and \"" + << needle << "\" matches \"" << r.pattern.pattern() << '"'; + break; + case TypeRejection::Invalid: + break; + } + return result; +} + +// qtdocgenerator.cpp + +QString msgTagWarning(const QXmlStreamReader &reader, const QString &context, + const QString &tag, const QString &message) +{ + QString result; + QTextStream str(&result); + str << "While handling <"; + const QStringRef currentTag = reader.name(); + if (currentTag.isEmpty()) + str << tag; + else + str << currentTag; + str << "> in " << context << ", line "<< reader.lineNumber() + << ": " << message; + return result; +} + +QString msgFallbackWarning(const QXmlStreamReader &reader, const QString &context, + const QString &tag, const QString &location, + const QString &identifier, const QString &fallback) +{ + QString message = QLatin1String("Falling back to \"") + + QDir::toNativeSeparators(fallback) + QLatin1String("\" for \"") + + location + QLatin1Char('"'); + if (!identifier.isEmpty()) + message += QLatin1String(" [") + identifier + QLatin1Char(']'); + return msgTagWarning(reader, context, tag, message); +} diff --git a/sources/shiboken2/ApiExtractor/messages.h b/sources/shiboken2/ApiExtractor/messages.h new file mode 100644 index 000000000..539332aef --- /dev/null +++ b/sources/shiboken2/ApiExtractor/messages.h @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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 MESSAGES_H +#define MESSAGES_H + +#include "abstractmetalang_typedefs.h" +#include "parser/codemodel_fwd.h" +#include "typesystem_typedefs.h" + +#include <QtCore/QMap> +#include <QtCore/QString> +#include <QtCore/QVector> + +class TypeEntry; +class TypeInfo; +struct TypeRejection; + +QT_FORWARD_DECLARE_CLASS(QDir) +QT_FORWARD_DECLARE_CLASS(QFile) +QT_FORWARD_DECLARE_CLASS(QXmlStreamReader) + +QString msgNoFunctionForModification(const QString &signature, + const QString &originalSignature, + const QString &className, + const QStringList &possibleSignatures, + const AbstractMetaFunctionList &allFunctions); + +QString msgNoEnumTypeEntry(const EnumModelItem &enumItem, + const QString &className); + + +QString msgNoEnumTypeConflict(const EnumModelItem &enumItem, + const QString &className, + const TypeEntry *t); + +QString msgUnmatchedParameterType(const ArgumentModelItem &arg, int n, + const QString &why); + +QString msgUnmatchedReturnType(const FunctionModelItem &functionItem, + const QString &why); + +QString msgSkippingFunction(const FunctionModelItem &functionItem, + const QString &signature, const QString &why); + +QString msgCannotSetArrayUsage(const QString &function, int i, const QString &reason); + +QString msgUnableToTranslateType(const QString &t, const QString &why); + +QString msgUnableToTranslateType(const TypeInfo &typeInfo, + const QString &why); + +QString msgCannotFindTypeEntry(const QString &t); + +QString msgCannotTranslateTemplateArgument(int i, + const TypeInfo &typeInfo, + const QString &why); + +QString msgDisallowThread(const AbstractMetaFunction *f); + +QString msgCannotFindDocumentation(const QString &fileName, + const char *what, const QString &name, + const QString &query); + +QString msgCannotFindDocumentation(const QString &fileName, + const AbstractMetaClass *metaClass, + const AbstractMetaFunction *function, + const QString &query); + +QString msgCannotFindDocumentation(const QString &fileName, + const AbstractMetaClass *metaClass, + const AbstractMetaEnum *e, + const QString &query); + +QString msgCannotFindDocumentation(const QString &fileName, + const AbstractMetaClass *metaClass, + const AbstractMetaField *f, + const QString &query); + +QString msgXpathDocModificationError(const DocModificationList& mods, + const QString &what); + +QString msgCannotOpenForReading(const QFile &f); + +QString msgCannotOpenForWriting(const QFile &f); + +QString msgCannotUseEnumAsInt(const QString &name); + +QString msgLeftOverArguments(const QMap<QString, QString> &remainingArgs); + +QString msgInvalidVersion(const QString &package, const QString &version); + +QString msgCyclicDependency(const QString &funcName, const QString &graphName, + const QVector<const AbstractMetaFunction *> &involvedConversions); + +QString msgUnknownOperator(const AbstractMetaFunction* func); + +QString msgWrongIndex(const char *varName, const QString &capture, + const AbstractMetaFunction *func); + +QString msgCannotFindType(const QString &type, const QString &variable, + const QString &why); + +QString msgCannotBuildMetaType(const QString &s); + +QString msgCouldNotFindMinimalConstructor(const QString &where, const QString &type); + +QString msgRejectReason(const TypeRejection &r, const QString &needle = QString()); + +QString msgTagWarning(const QXmlStreamReader &reader, const QString &context, + const QString &tag, const QString &message); + +QString msgFallbackWarning(const QXmlStreamReader &reader, const QString &context, + const QString &tag, const QString &location, + const QString &identifier, const QString &fallback); + +#endif // MESSAGES_H diff --git a/sources/shiboken2/ApiExtractor/parser/codemodel.cpp b/sources/shiboken2/ApiExtractor/parser/codemodel.cpp index d862692dd..8bc9b24ac 100644 --- a/sources/shiboken2/ApiExtractor/parser/codemodel.cpp +++ b/sources/shiboken2/ApiExtractor/parser/codemodel.cpp @@ -29,11 +29,15 @@ #include "codemodel.h" + +#include <clangparser/clangutils.h> + #include <algorithm> #include <functional> #include <iostream> #include <QDebug> #include <QDir> +#include <QtCore/QStack> // Predicate to find an item by name in a list of QSharedPointer<Item> template <class T> class ModelItemNamePredicate : public std::unary_function<bool, QSharedPointer<T> > @@ -140,16 +144,18 @@ TypeInfo TypeInfo::combine(const TypeInfo &__lhs, const TypeInfo &__rhs) __result.setVolatile(__result.isVolatile() || __rhs.isVolatile()); if (__rhs.referenceType() > __result.referenceType()) __result.setReferenceType(__rhs.referenceType()); - __result.setIndirections(__result.indirections() + __rhs.indirections()); + __result.m_indirections.append(__rhs.m_indirections); __result.setArrayElements(__result.arrayElements() + __rhs.arrayElements()); + __result.m_instantiations.append(__rhs.m_instantiations); return __result; } bool TypeInfo::isVoid() const { - return m_indirections == 0 && m_referenceType == NoReference + return m_indirections.isEmpty() && m_referenceType == NoReference && m_arguments.isEmpty() && m_arrayElements.isEmpty() + && m_instantiations.isEmpty() && m_qualifiedName.size() == 1 && m_qualifiedName.constFirst() == QLatin1String("void"); } @@ -193,19 +199,76 @@ TypeInfo TypeInfo::resolveType(CodeModelItem __item, TypeInfo const &__type, Cod return otherType; } +// Handler for clang::parseTemplateArgumentList() that populates +// TypeInfo::m_instantiations +class TypeInfoTemplateArgumentHandler : + public std::binary_function<void, int, const QStringRef &> +{ +public: + explicit TypeInfoTemplateArgumentHandler(TypeInfo *t) + { + m_parseStack.append(t); + } + + void operator()(int level, const QStringRef &name) + { + if (level > m_parseStack.size()) { + Q_ASSERT(!top()->m_instantiations.isEmpty()); + m_parseStack.push(&top()->m_instantiations.back()); + } + while (level < m_parseStack.size()) + m_parseStack.pop(); + TypeInfo instantiation; + instantiation.setQualifiedName(qualifiedName(name)); + top()->addInstantiation(instantiation); + } + +private: + TypeInfo *top() const { return m_parseStack.back(); } + + static QStringList qualifiedName(const QStringRef &name) + { + QStringList result; + const QVector<QStringRef> nameParts = name.split(QLatin1String("::")); + result.reserve(nameParts.size()); + for (const QStringRef &p : nameParts) + result.append(p.toString()); + return result; + } + + QStack<TypeInfo *> m_parseStack; +}; + +QPair<int, int> TypeInfo::parseTemplateArgumentList(const QString &l, int from) +{ + return clang::parseTemplateArgumentList(l, clang::TemplateArgumentHandler(TypeInfoTemplateArgumentHandler(this)), from); +} + QString TypeInfo::toString() const { QString tmp; - - tmp += m_qualifiedName.join(QLatin1String("::")); if (isConstant()) - tmp += QLatin1String(" const"); + tmp += QLatin1String("const "); if (isVolatile()) - tmp += QLatin1String(" volatile"); + tmp += QLatin1String("volatile "); - if (indirections()) - tmp += QString(indirections(), QLatin1Char('*')); + tmp += m_qualifiedName.join(QLatin1String("::")); + + if (const int instantiationCount = m_instantiations.size()) { + tmp += QLatin1Char('<'); + for (int i = 0; i < instantiationCount; ++i) { + if (i) + tmp += QLatin1String(", "); + tmp += m_instantiations.at(i).toString(); + } + if (tmp.endsWith(QLatin1Char('>'))) + tmp += QLatin1Char(' '); + tmp += QLatin1Char('>'); + } + + for (Indirection i : m_indirections) + tmp.append(indirectionKeyword(i)); switch (referenceType()) { case NoReference: @@ -238,20 +301,6 @@ QString TypeInfo::toString() const return tmp; } -QStringList TypeInfo::instantiationName() const -{ - QStringList result = m_qualifiedName; - if (const int argumentCount = m_arguments.size()) { - QString &last = result.last(); - for (int i = 0; i < argumentCount; ++i) { - last += i ? QLatin1String(", ") : QLatin1String("< "); - last += m_arguments.at(i).toString(); - } - last += QLatin1String(" >"); - } - return result; -} - bool TypeInfo::operator==(const TypeInfo &other) const { if (arrayElements().count() != other.arrayElements().count()) @@ -269,7 +318,73 @@ bool TypeInfo::operator==(const TypeInfo &other) const return flags == other.flags && m_qualifiedName == other.m_qualifiedName - && (!m_functionPointer || m_arguments == other.m_arguments); + && (!m_functionPointer || m_arguments == other.m_arguments) + && m_instantiations == other.m_instantiations; +} + +QString TypeInfo::indirectionKeyword(Indirection i) +{ + return i == Indirection::Pointer + ? QStringLiteral("*") : QStringLiteral("*const"); +} + +static inline QString constQualifier() { return QStringLiteral("const"); } +static inline QString volatileQualifier() { return QStringLiteral("volatile"); } + +bool TypeInfo::stripLeadingConst(QString *s) +{ + return stripLeadingQualifier(constQualifier(), s); +} + +bool TypeInfo::stripLeadingVolatile(QString *s) +{ + return stripLeadingQualifier(volatileQualifier(), s); +} + +bool TypeInfo::stripLeadingQualifier(const QString &qualifier, QString *s) +{ + // "const int x" + const int qualifierSize = qualifier.size(); + if (s->size() < qualifierSize + 1 || !s->startsWith(qualifier) + || !s->at(qualifierSize).isSpace()) { + return false; + } + s->remove(0, qualifierSize + 1); + while (!s->isEmpty() && s->at(0).isSpace()) + s->remove(0, 1); + return true; +} + +// Helper functionality to simplify a raw standard type as returned by +// clang_getCanonicalType() for g++ standard containers from +// "std::__cxx11::list<int, std::allocator<int> >" or +// "std::__1::list<int, std::allocator<int> >" -> "std::list<int>". + +bool TypeInfo::isStdType() const +{ + return m_qualifiedName.size() > 1 + && m_qualifiedName.constFirst() == QLatin1String("std"); +} + +static inline bool discardStdType(const QString &name) +{ + return name == QLatin1String("allocator") || name == QLatin1String("less"); +} + +void TypeInfo::simplifyStdType() +{ + if (isStdType()) { + if (m_qualifiedName.at(1).startsWith(QLatin1String("__"))) + m_qualifiedName.removeAt(1); + for (int t = m_instantiations.size() - 1; t >= 0; --t) { + if (m_instantiations.at(t).isStdType()) { + if (discardStdType(m_instantiations.at(t).m_qualifiedName.constLast())) + m_instantiations.removeAt(t); + else + m_instantiations[t].simplifyStdType(); + } + } + } } #ifndef QT_NO_DEBUG_STREAM @@ -292,8 +407,11 @@ void TypeInfo::formatDebug(QDebug &d) const d << ", [const]"; if (m_volatile) d << ", [volatile]"; - if (m_indirections) - d << ", indirections=" << m_indirections; + if (!m_indirections.isEmpty()) { + d << ", indirections="; + for (auto i : m_indirections) + d << ' ' << TypeInfo::indirectionKeyword(i); + } switch (m_referenceType) { case NoReference: break; @@ -304,6 +422,11 @@ void TypeInfo::formatDebug(QDebug &d) const d << ", [rvalref]"; break; } + if (!m_instantiations.isEmpty()) { + d << ", template<"; + formatSequence(d, m_instantiations.begin(), m_instantiations.end()); + d << '>'; + } if (m_functionPointer) { d << ", function ptr("; formatSequence(d, m_arguments.begin(), m_arguments.end()); @@ -358,9 +481,7 @@ _CodeModelItem::_CodeModelItem(CodeModel *model, const QString &name, int kind) { } -_CodeModelItem::~_CodeModelItem() -{ -} +_CodeModelItem::~_CodeModelItem() = default; int _CodeModelItem::kind() const { @@ -532,9 +653,7 @@ QDebug operator<<(QDebug d, const _CodeModelItem *t) #endif // !QT_NO_DEBUG_STREAM // --------------------------------------------------------------------------- -_ClassModelItem::~_ClassModelItem() -{ -} +_ClassModelItem::~_ClassModelItem() = default; TemplateParameterList _ClassModelItem::templateParameters() const { @@ -624,9 +743,7 @@ FunctionModelItem _ScopeModelItem::declaredFunction(FunctionModelItem item) return FunctionModelItem(); } -_ScopeModelItem::~_ScopeModelItem() -{ -} +_ScopeModelItem::~_ScopeModelItem() = default; void _ScopeModelItem::addEnumsDeclaration(const QString &enumsDeclaration) { @@ -770,14 +887,6 @@ _NamespaceModelItem::~_NamespaceModelItem() { } -QSet<NamespaceModelItem> _NamespaceModelItem::uniqueNamespaces() const -{ - QSet<NamespaceModelItem> result; - for (const NamespaceModelItem &n : m_namespaces) - result.insert(n); - return result; -} - void _NamespaceModelItem::addNamespace(NamespaceModelItem item) { m_namespaces.append(item); @@ -835,11 +944,9 @@ void _ArgumentModelItem::formatDebug(QDebug &d) const } #endif // !QT_NO_DEBUG_STREAM // --------------------------------------------------------------------------- -_FunctionModelItem::~_FunctionModelItem() -{ -} +_FunctionModelItem::~_FunctionModelItem() = default; -bool _FunctionModelItem::isSimilar(FunctionModelItem other) const +bool _FunctionModelItem::isSimilar(const FunctionModelItem &other) const { if (name() != other->name()) return false; @@ -871,7 +978,7 @@ ArgumentList _FunctionModelItem::arguments() const return m_arguments; } -void _FunctionModelItem::addArgument(ArgumentModelItem item) +void _FunctionModelItem::addArgument(const ArgumentModelItem& item) { m_arguments.append(item); } @@ -896,6 +1003,21 @@ void _FunctionModelItem::setVariadics(bool isVariadics) m_isVariadics = isVariadics; } +bool _FunctionModelItem::isNoExcept() const +{ + return m_exceptionSpecification == ExceptionSpecification::NoExcept; +} + +ExceptionSpecification _FunctionModelItem::exceptionSpecification() const +{ + return m_exceptionSpecification; +} + +void _FunctionModelItem::setExceptionSpecification(ExceptionSpecification e) +{ + m_exceptionSpecification = e; +} + bool _FunctionModelItem::isDeleted() const { return m_isDeleted; @@ -991,7 +1113,7 @@ void _FunctionModelItem::setInvokable(bool isInvokable) void _FunctionModelItem::formatDebug(QDebug &d) const { _MemberModelItem::formatDebug(d); - d << ", type=" << m_functionType; + d << ", type=" << m_functionType << ", exspec=" << int(m_exceptionSpecification); if (m_isDeleted) d << " [deleted!]"; if (m_isInline) @@ -1091,9 +1213,7 @@ void _EnumModelItem::formatDebug(QDebug &d) const #endif // !QT_NO_DEBUG_STREAM // --------------------------------------------------------------------------- -_EnumeratorModelItem::~_EnumeratorModelItem() -{ -} +_EnumeratorModelItem::~_EnumeratorModelItem() = default; QString _EnumeratorModelItem::stringValue() const { @@ -1114,9 +1234,7 @@ void _EnumeratorModelItem::formatDebug(QDebug &d) const #endif // !QT_NO_DEBUG_STREAM // --------------------------------------------------------------------------- -_TemplateParameterModelItem::~_TemplateParameterModelItem() -{ -} +_TemplateParameterModelItem::~_TemplateParameterModelItem() = default; TypeInfo _TemplateParameterModelItem::type() const { @@ -1164,9 +1282,7 @@ CodeModel::AccessPolicy _MemberModelItem::accessPolicy() const return m_accessPolicy; } -_MemberModelItem::~_MemberModelItem() -{ -} +_MemberModelItem::~_MemberModelItem() = default; void _MemberModelItem::setAccessPolicy(CodeModel::AccessPolicy accessPolicy) { diff --git a/sources/shiboken2/ApiExtractor/parser/codemodel.h b/sources/shiboken2/ApiExtractor/parser/codemodel.h index ac1fe26c1..0296a8cb2 100644 --- a/sources/shiboken2/ApiExtractor/parser/codemodel.h +++ b/sources/shiboken2/ApiExtractor/parser/codemodel.h @@ -36,6 +36,7 @@ #include "enumvalue.h" #include <QtCore/QHash> +#include <QtCore/QPair> #include <QtCore/QSet> #include <QtCore/QString> #include <QtCore/QStringList> @@ -100,6 +101,8 @@ class TypeInfo { friend class TypeParser; public: + typedef QVector<Indirection> Indirections; + TypeInfo() : flags(0), m_referenceType(NoReference) {} QStringList qualifiedName() const @@ -137,14 +140,16 @@ public: ReferenceType referenceType() const { return m_referenceType; } void setReferenceType(ReferenceType r) { m_referenceType = r; } - int indirections() const - { - return m_indirections; - } + Indirections indirectionsV() const { return m_indirections; } + void setIndirectionsV(const Indirections &i) { m_indirections = i; } + void addIndirection(Indirection i) { m_indirections.append(i); } + + // "Legacy", rename? + int indirections() const { return m_indirections.size(); } void setIndirections(int indirections) { - m_indirections = indirections; + m_indirections = Indirections(indirections, Indirection::Pointer); } bool isFunctionPointer() const @@ -165,6 +170,8 @@ public: m_arrayElements = arrayElements; } + void addArrayElement(const QString &a) { m_arrayElements.append(a); } + QVector<TypeInfo> arguments() const { return m_arguments; } void setArguments(const QVector<TypeInfo> &arguments); @@ -174,6 +181,15 @@ public: m_arguments.append(arg); } + QVector<TypeInfo> instantiations() const { return m_instantiations; } + void setInstantiations(const QVector<TypeInfo> &i) { m_instantiations = i; } + void addInstantiation(const TypeInfo &i) { m_instantiations.append(i); } + void clearInstantiations() { m_instantiations.clear(); } + + bool isStdType() const; + + QPair<int, int> parseTemplateArgumentList(const QString &l, int from = 0); + bool operator==(const TypeInfo &other) const; bool operator!=(const TypeInfo &other) const @@ -185,8 +201,6 @@ public: QString toString() const; - QStringList instantiationName() const; - static TypeInfo combine(const TypeInfo &__lhs, const TypeInfo &__rhs); static TypeInfo resolveType(TypeInfo const &__type, CodeModelItem __scope); @@ -194,12 +208,24 @@ public: void formatDebug(QDebug &d) const; #endif + static QString indirectionKeyword(Indirection i); + + static bool stripLeadingConst(QString *s); + static bool stripLeadingVolatile(QString *s); + static bool stripLeadingQualifier(const QString &qualifier, QString *s); + + void simplifyStdType(); + private: + friend class TypeInfoTemplateArgumentHandler; + static TypeInfo resolveType(CodeModelItem item, TypeInfo const &__type, CodeModelItem __scope); QStringList m_qualifiedName; QStringList m_arrayElements; QVector<TypeInfo> m_arguments; + QVector<TypeInfo> m_instantiations; + Indirections m_indirections; union { uint flags; @@ -208,8 +234,7 @@ private: uint m_constant: 1; uint m_volatile: 1; uint m_functionPointer: 1; - uint m_indirections: 6; - uint m_padding: 23; + uint m_padding: 29; }; }; @@ -409,8 +434,7 @@ public: : _ScopeModelItem(model, name, kind) {} ~_NamespaceModelItem(); - NamespaceList namespaces() const { return m_namespaces; } - QSet<NamespaceModelItem> uniqueNamespaces() const; + const NamespaceList &namespaces() const { return m_namespaces; } void addNamespace(NamespaceModelItem item); @@ -547,7 +571,7 @@ public: ArgumentList arguments() const; - void addArgument(ArgumentModelItem item); + void addArgument(const ArgumentModelItem& item); CodeModel::FunctionType functionType() const; void setFunctionType(CodeModel::FunctionType functionType); @@ -582,7 +606,13 @@ public: bool isVariadics() const; void setVariadics(bool isVariadics); - bool isSimilar(FunctionModelItem other) const; + + bool isSimilar(const FunctionModelItem &other) const; + + bool isNoExcept() const; + + ExceptionSpecification exceptionSpecification() const; + void setExceptionSpecification(ExceptionSpecification e); #ifndef QT_NO_DEBUG_STREAM void formatDebug(QDebug &d) const override; @@ -606,6 +636,7 @@ private: }; uint m_flags; }; + ExceptionSpecification m_exceptionSpecification = ExceptionSpecification::Unknown; }; class _VariableModelItem: public _MemberModelItem diff --git a/sources/shiboken2/ApiExtractor/parser/codemodel_enums.h b/sources/shiboken2/ApiExtractor/parser/codemodel_enums.h index b8a10ba93..1713ba42f 100644 --- a/sources/shiboken2/ApiExtractor/parser/codemodel_enums.h +++ b/sources/shiboken2/ApiExtractor/parser/codemodel_enums.h @@ -41,4 +41,17 @@ enum EnumKind { EnumClass // C++ 11 : enum class Foo { value1, value2 } }; +enum class Indirection +{ + Pointer, // int * + ConstPointer // int *const +}; + +enum class ExceptionSpecification +{ + Unknown, + NoExcept, + Throws +}; + #endif // CODEMODEL_ENUMS_H diff --git a/sources/shiboken2/ApiExtractor/parser/enumvalue.h b/sources/shiboken2/ApiExtractor/parser/enumvalue.h index 4905e89ba..ea30c39bb 100644 --- a/sources/shiboken2/ApiExtractor/parser/enumvalue.h +++ b/sources/shiboken2/ApiExtractor/parser/enumvalue.h @@ -49,6 +49,7 @@ public: Type type() { return m_type; } qint64 value() const { return m_value; } quint64 unsignedValue() const { return m_unsignedValue; } + bool isNullValue() const { return m_type == Signed ? m_value == 0 : m_unsignedValue == 0u; } void setValue(qint64 v); void setUnsignedValue(quint64 v); diff --git a/sources/shiboken2/ApiExtractor/qtdocparser.cpp b/sources/shiboken2/ApiExtractor/qtdocparser.cpp index b0058d6ea..809760450 100644 --- a/sources/shiboken2/ApiExtractor/qtdocparser.cpp +++ b/sources/shiboken2/ApiExtractor/qtdocparser.cpp @@ -28,6 +28,7 @@ #include "qtdocparser.h" #include "abstractmetalang.h" +#include "messages.h" #include "reporthandler.h" #include "typesystem.h" diff --git a/sources/shiboken2/ApiExtractor/tests/CMakeLists.txt b/sources/shiboken2/ApiExtractor/tests/CMakeLists.txt index 860a37d9d..e100ef493 100644 --- a/sources/shiboken2/ApiExtractor/tests/CMakeLists.txt +++ b/sources/shiboken2/ApiExtractor/tests/CMakeLists.txt @@ -4,13 +4,18 @@ find_package(Qt5Test) find_package(Qt5Xml) find_package(Qt5XmlPatterns) +set(CMAKE_AUTORCC ON) + macro(declare_test testname) # gone: qt4_automoc("${testname}.cpp") - if (EXISTS "${testname}.h") - add_executable(${testname} "${testname}.h ${testname}.cpp") - else () - add_executable(${testname} "${testname}.cpp") + set(SOURCES "${testname}.cpp") + if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${testname}.h") + list(APPEND SOURCES "${testname}.h") + endif () + if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${testname}.qrc") + list(APPEND SOURCES "${testname}.qrc") endif () + add_executable(${testname} ${SOURCES}) include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${apiextractor_SOURCE_DIR} @@ -35,8 +40,6 @@ declare_test(testabstractmetatype) declare_test(testaddfunction) declare_test(testarrayargument) declare_test(testcodeinjection) -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/utf8code.txt" - "${CMAKE_CURRENT_BINARY_DIR}/utf8code.txt" COPYONLY) declare_test(testcontainer) declare_test(testconversionoperator) declare_test(testconversionruletag) @@ -68,7 +71,5 @@ declare_test(testvoidarg) declare_test(testtyperevision) if (NOT DISABLE_DOCSTRINGS) declare_test(testmodifydocumentation) - configure_file("${CMAKE_CURRENT_SOURCE_DIR}/a.xml" - "${CMAKE_CURRENT_BINARY_DIR}/a.xml" COPYONLY) endif() diff --git a/sources/shiboken2/ApiExtractor/tests/injectedcode.txt b/sources/shiboken2/ApiExtractor/tests/injectedcode.txt new file mode 100644 index 000000000..872898810 --- /dev/null +++ b/sources/shiboken2/ApiExtractor/tests/injectedcode.txt @@ -0,0 +1,5 @@ +// Bla +// @snippet label +code line +// @snippet label +// Bla diff --git a/sources/shiboken2/ApiExtractor/tests/testabstractmetatype.cpp b/sources/shiboken2/ApiExtractor/tests/testabstractmetatype.cpp index 7f1361a7d..fc67ebba5 100644 --- a/sources/shiboken2/ApiExtractor/tests/testabstractmetatype.cpp +++ b/sources/shiboken2/ApiExtractor/tests/testabstractmetatype.cpp @@ -31,6 +31,37 @@ #include "testutil.h" #include <abstractmetalang.h> #include <typesystem.h> +#include <parser/codemodel.h> +#include <typeparser.h> + +void TestAbstractMetaType::parsing_data() +{ + QTest::addColumn<QString>("input"); + QTest::addColumn<QString>("output"); + QTest::newRow("primitive") + << QString::fromLatin1("int") << QString::fromLatin1("int"); + QTest::newRow("ref") + << QString::fromLatin1("int &") << QString::fromLatin1("int&"); + QTest::newRow("pointer") + << QString::fromLatin1("int **") << QString::fromLatin1("int**"); + QTest::newRow("const ref") + << QString::fromLatin1("const int &") << QString::fromLatin1("const int&"); + QTest::newRow("const pointer") + << QString::fromLatin1("const int **") << QString::fromLatin1("const int**"); + QTest::newRow("const pointer const") + << QString::fromLatin1("const int *const*") << QString::fromLatin1("const int*const*"); +} + +void TestAbstractMetaType::parsing() +{ + QFETCH(QString, input); + QFETCH(QString, output); + QString errorMessage; + const TypeInfo ti = TypeParser::parse(input, &errorMessage); + QVERIFY2(errorMessage.isEmpty(), qPrintable(errorMessage)); + const QString actual = ti.toString(); + QCOMPARE(actual, output); +} void TestAbstractMetaType::testConstCharPtrType() { @@ -72,7 +103,8 @@ void TestAbstractMetaType::testApiVersionSupported() <function signature='justAtest2()' since='1.1'/>\n\ <function signature='justAtest3()'/>\n\ </typesystem>\n"; - QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false, "1.0")); + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, + false, QLatin1String("1.0"))); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); @@ -90,7 +122,8 @@ void TestAbstractMetaType::testApiVersionNotSupported() const char* xmlCode = "<typesystem package='Foo'>\n\ <value-type name='object' since='0.1'/>\n\ </typesystem>\n"; - QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, true, "0.1")); + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, + true, QLatin1String("0.1"))); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); diff --git a/sources/shiboken2/ApiExtractor/tests/testabstractmetatype.h b/sources/shiboken2/ApiExtractor/tests/testabstractmetatype.h index b2aa7544f..b39a27a54 100644 --- a/sources/shiboken2/ApiExtractor/tests/testabstractmetatype.h +++ b/sources/shiboken2/ApiExtractor/tests/testabstractmetatype.h @@ -35,6 +35,8 @@ class TestAbstractMetaType : public QObject { Q_OBJECT private slots: + void parsing_data(); + void parsing(); void testConstCharPtrType(); void testCharType(); void testTypedef(); diff --git a/sources/shiboken2/ApiExtractor/tests/testaddfunction.cpp b/sources/shiboken2/ApiExtractor/tests/testaddfunction.cpp index 2a953243e..db49942c9 100644 --- a/sources/shiboken2/ApiExtractor/tests/testaddfunction.cpp +++ b/sources/shiboken2/ApiExtractor/tests/testaddfunction.cpp @@ -366,7 +366,8 @@ void TestAddFunction::testAddFunctionWithApiVersion() <inject-code class='target' position='beginning'>custom_code();</inject-code>\n\ </add-function>\n\ </typesystem>\n"; - QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, true, "0.1")); + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, + true, QLatin1String("0.1"))); QVERIFY(!builder.isNull()); AbstractMetaFunctionList globalFuncs = builder->globalFunctions(); QCOMPARE(globalFuncs.count(), 1); diff --git a/sources/shiboken2/ApiExtractor/tests/testcodeinjection.cpp b/sources/shiboken2/ApiExtractor/tests/testcodeinjection.cpp index 7bbde3bd4..9f71b495a 100644 --- a/sources/shiboken2/ApiExtractor/tests/testcodeinjection.cpp +++ b/sources/shiboken2/ApiExtractor/tests/testcodeinjection.cpp @@ -34,19 +34,43 @@ #include <abstractmetalang.h> #include <typesystem.h> -void TestCodeInjections::testReadFileUtf8() +void TestCodeInjections::testReadFile_data() { + QTest::addColumn<QString>("filePath"); + QTest::addColumn<QString>("snippet"); + QTest::addColumn<QString>("expected"); + + QTest::newRow("utf8") + << QString::fromLatin1(":/utf8code.txt") + << QString() + << QString::fromUtf8("\xC3\xA1\xC3\xA9\xC3\xAD\xC3\xB3\xC3\xBA"); + + QTest::newRow("snippet") + << QString::fromLatin1(":/injectedcode.txt") + << QString::fromLatin1("label") + << QString::fromLatin1("code line"); +} + +void TestCodeInjections::testReadFile() +{ + QFETCH(QString, filePath); + QFETCH(QString, snippet); + QFETCH(QString, expected); + const char* cppCode ="struct A {};\n"; int argc = 0; char *argv[] = {NULL}; QCoreApplication app(argc, argv); - QString filePath = QDir::currentPath(); + + QString attribute = QLatin1String("file='") + filePath + QLatin1Char('\''); + if (!snippet.isEmpty()) + attribute += QLatin1String(" snippet='") + snippet + QLatin1Char('\''); + QString xmlCode = QLatin1String("\ <typesystem package=\"Foo\">\n\ <value-type name='A'>\n\ - <conversion-rule file='") + filePath + QLatin1String("/utf8code.txt'/>\n\ - <inject-code class='target' file='") + filePath - + QLatin1String("/utf8code.txt'/>\n\ + <conversion-rule ") + attribute + QLatin1String("/>\n\ + <inject-code class='target' ") + attribute + QLatin1String("/>\n\ </value-type>\n\ <value-type name='A::B'/>\n\ </typesystem>\n"); @@ -56,10 +80,9 @@ void TestCodeInjections::testReadFileUtf8() const AbstractMetaClass *classA = AbstractMetaClass::findClass(classes, QLatin1String("A")); QCOMPARE(classA->typeEntry()->codeSnips().count(), 1); QString code = classA->typeEntry()->codeSnips().first().code(); - QString utf8Data = QString::fromUtf8("\xC3\xA1\xC3\xA9\xC3\xAD\xC3\xB3\xC3\xBA"); - QVERIFY(code.indexOf(utf8Data) != -1); + QVERIFY(code.indexOf(expected) != -1); code = classA->typeEntry()->conversionRule(); - QVERIFY(code.indexOf(utf8Data) != -1); + QVERIFY(code.indexOf(expected) != -1); } void TestCodeInjections::testInjectWithValidApiVersion() @@ -74,7 +97,8 @@ void TestCodeInjections::testInjectWithValidApiVersion() </value-type>\n\ </typesystem>\n"; - QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, true, "1.0")); + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, + true, QLatin1String("1.0"))); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); AbstractMetaClass* classA = AbstractMetaClass::findClass(classes, QLatin1String("A")); @@ -93,7 +117,8 @@ void TestCodeInjections::testInjectWithInvalidApiVersion() </value-type>\n\ </typesystem>\n"; - QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, true, "0.1")); + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, + true, QLatin1String("0.1"))); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); diff --git a/sources/shiboken2/ApiExtractor/tests/testcodeinjection.h b/sources/shiboken2/ApiExtractor/tests/testcodeinjection.h index bd5e7ece1..1ac873970 100644 --- a/sources/shiboken2/ApiExtractor/tests/testcodeinjection.h +++ b/sources/shiboken2/ApiExtractor/tests/testcodeinjection.h @@ -37,7 +37,8 @@ class TestCodeInjections : public QObject { Q_OBJECT private slots: - void testReadFileUtf8(); + void testReadFile_data(); + void testReadFile(); void testInjectWithValidApiVersion(); void testInjectWithInvalidApiVersion(); }; diff --git a/sources/shiboken2/ApiExtractor/tests/testcodeinjection.qrc b/sources/shiboken2/ApiExtractor/tests/testcodeinjection.qrc new file mode 100644 index 000000000..fd7616bd2 --- /dev/null +++ b/sources/shiboken2/ApiExtractor/tests/testcodeinjection.qrc @@ -0,0 +1,6 @@ +<RCC> + <qresource> + <file>utf8code.txt</file> + <file>injectedcode.txt</file> + </qresource> +</RCC> diff --git a/sources/shiboken2/ApiExtractor/tests/testdroptypeentries.cpp b/sources/shiboken2/ApiExtractor/tests/testdroptypeentries.cpp index b46c23f56..6abebb922 100644 --- a/sources/shiboken2/ApiExtractor/tests/testdroptypeentries.cpp +++ b/sources/shiboken2/ApiExtractor/tests/testdroptypeentries.cpp @@ -70,7 +70,8 @@ void TestDropTypeEntries::testDropEntries() droppedEntries << QLatin1String("Foo.ObjectB") << QLatin1String("Foo.NamespaceA.InnerClassA"); droppedEntries << QLatin1String("Foo.NamespaceB") << QLatin1String("Foo.EnumB") << QLatin1String("Foo.funcB()"); droppedEntries << QLatin1String("Foo.NamespaceA.InnerNamespaceA"); - QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false, Q_NULLPTR, droppedEntries)); + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false, + QString(), droppedEntries)); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); @@ -129,7 +130,8 @@ static const char* xmlCode2 = "\ void TestDropTypeEntries::testDropEntryWithChildTags() { QStringList droppedEntries(QLatin1String("Foo.ValueA")); - QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode2, xmlCode2, false, Q_NULLPTR, droppedEntries)); + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode2, xmlCode2, false, + QString(), droppedEntries)); QVERIFY(!builder.isNull()); QVERIFY(!AbstractMetaClass::findClass(builder->classes(), QLatin1String("ValueA"))); } diff --git a/sources/shiboken2/ApiExtractor/tests/testenum.cpp b/sources/shiboken2/ApiExtractor/tests/testenum.cpp index 87f2608a1..ebdcf8d81 100644 --- a/sources/shiboken2/ApiExtractor/tests/testenum.cpp +++ b/sources/shiboken2/ApiExtractor/tests/testenum.cpp @@ -104,7 +104,8 @@ void TestEnum::testEnumWithApiVersion() </value-type>\n\ </typesystem>\n"; - QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, true, "0.1")); + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, + true, QLatin1String("0.1"))); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); QCOMPARE(classes.count(), 1); diff --git a/sources/shiboken2/ApiExtractor/tests/testmodifydocumentation.cpp b/sources/shiboken2/ApiExtractor/tests/testmodifydocumentation.cpp index 7911a5eb1..f615befb4 100644 --- a/sources/shiboken2/ApiExtractor/tests/testmodifydocumentation.cpp +++ b/sources/shiboken2/ApiExtractor/tests/testmodifydocumentation.cpp @@ -60,7 +60,7 @@ R"(<typesystem package="Foo"> QCOMPARE(docMods[1].code().trimmed(), QLatin1String("<para>Some changed contents here</para>")); QCOMPARE(docMods[1].signature(), QString()); QtDocParser docParser; - docParser.setDocumentationDataDirectory(QDir::currentPath()); + docParser.setDocumentationDataDirectory(QLatin1String(":")); docParser.fillDocumentation(classA); const QString actualDocSimplified = classA->documentation().value().simplified(); diff --git a/sources/shiboken2/ApiExtractor/tests/testmodifydocumentation.qrc b/sources/shiboken2/ApiExtractor/tests/testmodifydocumentation.qrc new file mode 100644 index 000000000..76b1bfc61 --- /dev/null +++ b/sources/shiboken2/ApiExtractor/tests/testmodifydocumentation.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource> + <file>a.xml</file> + </qresource> +</RCC> diff --git a/sources/shiboken2/ApiExtractor/tests/testmodifyfunction.cpp b/sources/shiboken2/ApiExtractor/tests/testmodifyfunction.cpp index d0a0c9c7a..af24689fe 100644 --- a/sources/shiboken2/ApiExtractor/tests/testmodifyfunction.cpp +++ b/sources/shiboken2/ApiExtractor/tests/testmodifyfunction.cpp @@ -136,7 +136,8 @@ void TestModifyFunction::invalidateAfterUse() </object-type>\n\ <object-type name='E' />\n\ </typesystem>\n"; - QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false, "0.1")); + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, + false, QLatin1String("0.1"))); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); const AbstractMetaClass *classB = AbstractMetaClass::findClass(classes, QLatin1String("B")); @@ -208,7 +209,8 @@ void TestModifyFunction::testWithApiVersion() </modify-function>\n\ </object-type>\n\ </typesystem>\n"; - QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false, "0.1")); + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, + false, QLatin1String("0.1"))); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); AbstractMetaClass* classB = AbstractMetaClass::findClass(classes, QLatin1String("B")); @@ -220,6 +222,61 @@ void TestModifyFunction::testWithApiVersion() QVERIFY(func->ownership(func->ownerClass(), TypeSystem::TargetLangCode, 0) != TypeSystem::CppOwnership); } +void TestModifyFunction::testAllowThread() +{ + const char cppCode[] =R"CPP(\ +struct A { + void f1(); + void f2(); + void f3(); + int getter1() const; + int getter2() const; +}; +)CPP"; + + const char xmlCode[] = R"XML( +<typesystem package='Foo'> + <primitive-type name='int'/> + <object-type name='A'> + <modify-function signature='f2()' allow-thread='auto'/> + <modify-function signature='f3()' allow-thread='no'/> + <modify-function signature='getter2()const' allow-thread='yes'/> + </object-type> +</typesystem> +)XML"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, + false, QLatin1String("0.1"))); + QVERIFY(!builder.isNull()); + AbstractMetaClassList classes = builder->classes(); + const AbstractMetaClass *classA = AbstractMetaClass::findClass(classes, QLatin1String("A")); + QVERIFY(classA); + + // Nothing specified, true + const AbstractMetaFunction *f1 = classA->findFunction(QLatin1String("f1")); + QVERIFY(f1); + QVERIFY(f1->allowThread()); + + // 'auto' specified, should be true for nontrivial function + const AbstractMetaFunction *f2 = classA->findFunction(QLatin1String("f2")); + QVERIFY(f2); + QVERIFY(f2->allowThread()); + + // 'no' specified, should be false + const AbstractMetaFunction *f3 = classA->findFunction(QLatin1String("f3")); + QVERIFY(f3); + QVERIFY(!f3->allowThread()); + + // Nothing specified, should be false for simple getter + const AbstractMetaFunction *getter1 = classA->findFunction(QLatin1String("getter1")); + QVERIFY(getter1); + QVERIFY(!getter1->allowThread()); + + // Forced to true simple getter + const AbstractMetaFunction *getter2 = classA->findFunction(QLatin1String("getter2")); + QVERIFY(getter2); + QVERIFY(getter2->allowThread()); // Forced to true simple getter +} + void TestModifyFunction::testGlobalFunctionModification() { const char* cppCode ="\ @@ -258,4 +315,42 @@ void TestModifyFunction::testGlobalFunctionModification() QCOMPARE(arg->defaultValueExpression(), QLatin1String("A()")); } +void TestModifyFunction::testExceptionSpecification() +{ + const char cppCode[] = R"CPP( +struct A { + void unspecified(); + void nonThrowing() noexcept; + void throwing() throw(int); +}; +)CPP"; + const char xmlCode[] = R"XML( +<typesystem package="Foo"> + <primitive-type name='int'/> + <object-type name='A'> + <modify-function signature='throwing()' exception-handling='auto-on'/> + </object-type> +</typesystem>)XML"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(!builder.isNull()); + + const AbstractMetaClass *classA = AbstractMetaClass::findClass(builder->classes(), QLatin1String("A")); + QVERIFY(classA); + + const AbstractMetaFunction *f = classA->findFunction(QStringLiteral("unspecified")); + QVERIFY(f); + QCOMPARE(f->exceptionSpecification(), ExceptionSpecification::Unknown); + QVERIFY(!f->generateExceptionHandling()); + + f = classA->findFunction(QStringLiteral("nonThrowing")); + QVERIFY(f); + QCOMPARE(f->exceptionSpecification(), ExceptionSpecification::NoExcept); + QVERIFY(!f->generateExceptionHandling()); + + f = classA->findFunction(QStringLiteral("throwing")); + QVERIFY(f); + QCOMPARE(f->exceptionSpecification(), ExceptionSpecification::Throws); + QVERIFY(f->generateExceptionHandling()); +} + QTEST_APPLESS_MAIN(TestModifyFunction) diff --git a/sources/shiboken2/ApiExtractor/tests/testmodifyfunction.h b/sources/shiboken2/ApiExtractor/tests/testmodifyfunction.h index f116b5124..494f31991 100644 --- a/sources/shiboken2/ApiExtractor/tests/testmodifyfunction.h +++ b/sources/shiboken2/ApiExtractor/tests/testmodifyfunction.h @@ -37,10 +37,12 @@ class TestModifyFunction : public QObject private slots: void testOwnershipTransfer(); void testWithApiVersion(); + void testAllowThread(); void testRenameArgument_data(); void testRenameArgument(); void invalidateAfterUse(); void testGlobalFunctionModification(); + void testExceptionSpecification(); }; #endif diff --git a/sources/shiboken2/ApiExtractor/tests/testrefcounttag.cpp b/sources/shiboken2/ApiExtractor/tests/testrefcounttag.cpp index 11cda3317..38099c455 100644 --- a/sources/shiboken2/ApiExtractor/tests/testrefcounttag.cpp +++ b/sources/shiboken2/ApiExtractor/tests/testrefcounttag.cpp @@ -82,7 +82,8 @@ void TestRefCountTag::testWithApiVersion() </object-type>\n\ </typesystem>\n"; - QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false, "0.1")); + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, + false, QLatin1String("0.1"))); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); const AbstractMetaClass *classB = AbstractMetaClass::findClass(classes, QLatin1String("B")); diff --git a/sources/shiboken2/ApiExtractor/tests/testtemplates.cpp b/sources/shiboken2/ApiExtractor/tests/testtemplates.cpp index 8d869e3f9..b1b171bae 100644 --- a/sources/shiboken2/ApiExtractor/tests/testtemplates.cpp +++ b/sources/shiboken2/ApiExtractor/tests/testtemplates.cpp @@ -28,6 +28,7 @@ #include "testtemplates.h" #include <QtTest/QTest> +#include <QtCore/QTextStream> #include <QTemporaryFile> #include "testutil.h" #include <abstractmetalang.h> @@ -438,4 +439,122 @@ typedef Vector<int> IntVector; QCOMPARE(otherMethod->type()->cppSignature(), QLatin1String("Vector<int >")); } +// Perform checks on template inheritance; a typedef of a template class +// should result in rewritten types. +void TestTemplates::testTemplateTypeDefs_data() +{ + QTest::addColumn<QString>("cpp"); + QTest::addColumn<QString>("xml"); + + const char optionalClassDef[] = R"CPP( +template<class T> // Some value type similar to std::optional +class Optional { +public: + T value() const { return m_value; } + operator bool() const { return m_success; } + + T m_value; + bool m_success = false; +}; +)CPP"; + + const char xmlPrefix[] = R"XML( +<typesystem package='Foo'> + <primitive-type name='int'/> + <primitive-type name='bool'/> +)XML"; + + const char xmlOptionalDecl[] = "<value-type name='Optional' generate='no'/>\n"; + const char xmlOptionalIntDecl[] = "<value-type name='IntOptional'/>\n"; + const char xmlPostFix[] = "</typesystem>\n"; + + // Flat, global namespace + QString cpp; + QTextStream(&cpp) << optionalClassDef + << "typedef Optional<int> IntOptional;\n"; + QString xml; + QTextStream(&xml) << xmlPrefix << xmlOptionalDecl << xmlOptionalIntDecl + << "<typedef-type name='XmlIntOptional' source='Optional<int>'/>" + << xmlPostFix; + QTest::newRow("global-namespace") + << cpp << xml; + + // Typedef from namespace Std + cpp.clear(); + QTextStream(&cpp) << "namespace Std {\n" << optionalClassDef << "}\n" + << "typedef Std::Optional<int> IntOptional;\n"; + xml.clear(); + QTextStream(&xml) << xmlPrefix + << "<namespace-type name='Std'>\n" << xmlOptionalDecl + << "</namespace-type>\n" << xmlOptionalIntDecl + << "<typedef-type name='XmlIntOptional' source='Std::Optional<int>'/>" + << xmlPostFix; + QTest::newRow("namespace-Std") + << cpp << xml; + + // Typedef from nested class + cpp.clear(); + QTextStream(&cpp) << "class Outer {\npublic:\n" << optionalClassDef << "\n};\n" + << "typedef Outer::Optional<int> IntOptional;\n"; + xml.clear(); + QTextStream(&xml) << xmlPrefix + << "<object-type name='Outer'>\n" << xmlOptionalDecl + << "</object-type>\n" << xmlOptionalIntDecl + << "<typedef-type name='XmlIntOptional' source='Outer::Optional<int>'/>" + << xmlPostFix; + QTest::newRow("nested-class") + << cpp << xml; +} + +void TestTemplates::testTemplateTypeDefs() +{ + QFETCH(QString, cpp); + QFETCH(QString, xml); + + const QByteArray cppBa = cpp.toLocal8Bit(); + const QByteArray xmlBa = xml.toLocal8Bit(); + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppBa.constData(), xmlBa.constData(), true)); + QVERIFY(!builder.isNull()); + AbstractMetaClassList classes = builder->classes(); + + const AbstractMetaClass *optional = AbstractMetaClass::findClass(classes, QLatin1String("Optional")); + QVERIFY(optional); + + // Find the typedef'ed class + const AbstractMetaClass *optionalInt = + AbstractMetaClass::findClass(classes, QLatin1String("IntOptional")); + QVERIFY(optionalInt); + QCOMPARE(optionalInt->templateBaseClass(), optional); + + // Find the class typedef'ed in the typesystem XML + const AbstractMetaClass *xmlOptionalInt = + AbstractMetaClass::findClass(classes, QLatin1String("XmlIntOptional")); + QVERIFY(xmlOptionalInt); + QCOMPARE(xmlOptionalInt->templateBaseClass(), optional); + + // Check whether the value() method now has an 'int' return + const AbstractMetaFunction *valueMethod = + optionalInt->findFunction(QLatin1String("value")); + QVERIFY(valueMethod); + QCOMPARE(valueMethod->type()->cppSignature(), QLatin1String("int")); + + // ditto for typesystem XML + const AbstractMetaFunction *xmlValueMethod = + xmlOptionalInt->findFunction(QLatin1String("value")); + QVERIFY(xmlValueMethod); + QCOMPARE(xmlValueMethod->type()->cppSignature(), QLatin1String("int")); + + // Check whether the m_value field is of type 'int' + const AbstractMetaField *valueField = + optionalInt->findField(QLatin1String("m_value")); + QVERIFY(valueField); + QCOMPARE(valueField->type()->cppSignature(), QLatin1String("int")); + + // ditto for typesystem XML + const AbstractMetaField *xmlValueField = + xmlOptionalInt->findField(QLatin1String("m_value")); + QVERIFY(xmlValueField); + QCOMPARE(xmlValueField->type()->cppSignature(), QLatin1String("int")); +} + QTEST_APPLESS_MAIN(TestTemplates) diff --git a/sources/shiboken2/ApiExtractor/tests/testtemplates.h b/sources/shiboken2/ApiExtractor/tests/testtemplates.h index 3e1565933..df3de18b9 100644 --- a/sources/shiboken2/ApiExtractor/tests/testtemplates.h +++ b/sources/shiboken2/ApiExtractor/tests/testtemplates.h @@ -46,6 +46,8 @@ private slots: void testTemplateInheritanceMixedWithNamespaceAndForwardDeclaration(); void testTypedefOfInstantiationOfTemplateClass(); void testContainerTypeIncompleteArgument(); + void testTemplateTypeDefs_data(); + void testTemplateTypeDefs(); }; #endif diff --git a/sources/shiboken2/ApiExtractor/tests/testtyperevision.cpp b/sources/shiboken2/ApiExtractor/tests/testtyperevision.cpp index 1ec7ce025..a7e88e437 100644 --- a/sources/shiboken2/ApiExtractor/tests/testtyperevision.cpp +++ b/sources/shiboken2/ApiExtractor/tests/testtyperevision.cpp @@ -31,6 +31,7 @@ #include "testutil.h" #include <abstractmetalang.h> #include <typesystem.h> +#include <typedatabase.h> void TestTypeRevision::testRevisionAttr() { @@ -49,21 +50,55 @@ void TestTypeRevision::testRevisionAttr() QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); const AbstractMetaClass *rev0 = AbstractMetaClass::findClass(classes, QLatin1String("Rev_0")); - QCOMPARE(getTypeRevision(rev0->typeEntry()), 0); + QCOMPARE(rev0->typeEntry()->revision(), 0); const AbstractMetaClass *rev1 = AbstractMetaClass::findClass(classes, QLatin1String("Rev_1")); - QCOMPARE(getTypeRevision(rev1->typeEntry()), 1); + QCOMPARE(rev1->typeEntry()->revision(), 1); AbstractMetaClass *rev2 = AbstractMetaClass::findClass(classes, QLatin1String("Rev_2")); - QCOMPARE(getTypeRevision(rev2->typeEntry()), 2); + QCOMPARE(rev2->typeEntry()->revision(), 2); AbstractMetaEnum* rev3 = rev2->findEnum(QLatin1String("Rev_3")); - QCOMPARE(getTypeRevision(rev3->typeEntry()), 3); + QCOMPARE(rev3->typeEntry()->revision(), 3); FlagsTypeEntry* rev4 = rev3->typeEntry()->flags(); - QCOMPARE(getTypeRevision(rev4), 4); + QCOMPARE(rev4->revision(), 4); AbstractMetaEnum* rev5 = rev2->findEnum(QLatin1String("Rev_5")); - QCOMPARE(getTypeRevision(rev5->typeEntry()), 5); - QCOMPARE(getTypeRevision(rev5->typeEntry()->flags()), 5); + const EnumTypeEntry *revEnumTypeEntry = rev5->typeEntry(); + QCOMPARE(revEnumTypeEntry->revision(), 5); + QCOMPARE(revEnumTypeEntry->flags()->revision(), 5); +} + + +void TestTypeRevision::testVersion_data() +{ + QTest::addColumn<QString>("version"); + QTest::addColumn<int>("expectedClassCount"); + + QTest::newRow("none") << QString() << 2; + QTest::newRow("1.0") << QString::fromLatin1("1.0") << 1; // Bar20 excluded + QTest::newRow("2.0") << QString::fromLatin1("2.0") << 2; +} + +void TestTypeRevision::testVersion() +{ + QFETCH(QString, version); + QFETCH(int, expectedClassCount); + + const char cppCode[] = R"CPP( +class Bar {}; +class Bar20 {}; +)CPP"; + const char xmlCode[] = R"XML( +<typesystem package="Foo"> + <value-type name="Bar"/> + <value-type name="Bar20" since="2.0"/> +</typesystem> +)XML"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, true, version)); + QVERIFY(!builder.isNull()); + + QCOMPARE(builder->classes().size(), expectedClassCount); } QTEST_APPLESS_MAIN(TestTypeRevision) diff --git a/sources/shiboken2/ApiExtractor/tests/testtyperevision.h b/sources/shiboken2/ApiExtractor/tests/testtyperevision.h index 4dfa241e3..3832c3883 100644 --- a/sources/shiboken2/ApiExtractor/tests/testtyperevision.h +++ b/sources/shiboken2/ApiExtractor/tests/testtyperevision.h @@ -37,6 +37,8 @@ class TestTypeRevision : public QObject private slots: void testRevisionAttr(); + void testVersion_data(); + void testVersion(); }; #endif diff --git a/sources/shiboken2/ApiExtractor/tests/testutil.h b/sources/shiboken2/ApiExtractor/tests/testutil.h index 6152793f5..c6ad19d7e 100644 --- a/sources/shiboken2/ApiExtractor/tests/testutil.h +++ b/sources/shiboken2/ApiExtractor/tests/testutil.h @@ -40,20 +40,23 @@ namespace TestUtil { static AbstractMetaBuilder *parse(const char *cppCode, const char *xmlCode, bool silent = true, - const char *apiVersion = Q_NULLPTR, + const QString &apiVersion = QString(), const QStringList &dropTypeEntries = QStringList()) { ReportHandler::setSilent(silent); TypeDatabase* td = TypeDatabase::instance(true); - if (apiVersion && !td->setApiVersion(QLatin1String("*"), QLatin1String(apiVersion))) - return Q_NULLPTR; + if (apiVersion.isEmpty()) + TypeDatabase::clearApiVersions(); + else if (!td->setApiVersion(QLatin1String("*"), apiVersion)) + return nullptr; td->setDropTypeEntries(dropTypeEntries); QBuffer buffer; // parse typesystem buffer.setData(xmlCode); if (!buffer.open(QIODevice::ReadOnly)) return Q_NULLPTR; - td->parseFile(&buffer); + if (!td->parseFile(&buffer)) + return nullptr; buffer.close(); // parse C++ code QTemporaryFile tempSource(QDir::tempPath() + QLatin1String("/st_XXXXXX_main.cpp")); diff --git a/sources/shiboken2/ApiExtractor/typedatabase.cpp b/sources/shiboken2/ApiExtractor/typedatabase.cpp index 9529de40a..c0999e7ab 100644 --- a/sources/shiboken2/ApiExtractor/typedatabase.cpp +++ b/sources/shiboken2/ApiExtractor/typedatabase.cpp @@ -166,64 +166,66 @@ ContainerTypeEntry* TypeDatabase::findContainerType(const QString &name) const return 0; } -FunctionTypeEntry* TypeDatabase::findFunctionType(const QString& name) const +static bool inline useType(const TypeEntry *t) { - TypeEntry* entry = findType(name); - if (entry && entry->type() == TypeEntry::FunctionType) - return static_cast<FunctionTypeEntry*>(entry); - return 0; + return !t->isPrimitive() + || static_cast<const PrimitiveTypeEntry *>(t)->preferredTargetLangType(); } -TypeEntry* TypeDatabase::findType(const QString& name) const +FunctionTypeEntry* TypeDatabase::findFunctionType(const QString& name) const { - const TypeEntryList &entries = findTypes(name); + const auto entries = findTypes(name); for (TypeEntry *entry : entries) { - if (entry && - (!entry->isPrimitive() || static_cast<PrimitiveTypeEntry *>(entry)->preferredTargetLangType())) { - return entry; - } + if (entry->type() == TypeEntry::FunctionType && useType(entry)) + return static_cast<FunctionTypeEntry*>(entry); } return 0; } -TypeEntryList TypeDatabase::findTypes(const QString &name) const +const TypeSystemTypeEntry *TypeDatabase::findTypeSystemType(const QString &name) const { - return m_entries.value(name); + const auto entries = findTypes(name); + for (const TypeEntry *entry : entries) { + if (entry->type() == TypeEntry::TypeSystemType) + return static_cast<const TypeSystemTypeEntry *>(entry); + } + return nullptr; } -SingleTypeEntryHash TypeDatabase::entries() const +TypeEntry* TypeDatabase::findType(const QString& name) const { - TypeEntryHash entries = allEntries(); - - SingleTypeEntryHash returned; - for (TypeEntryHash::const_iterator it = entries.cbegin(), end = entries.cend(); it != end; ++it) - returned.insert(it.key(), findType(it.key())); + const auto entries = findTypes(name); + for (TypeEntry *entry : entries) { + if (useType(entry)) + return entry; + } + return nullptr; +} - return returned; +TypeEntryMultiMapConstIteratorRange TypeDatabase::findTypes(const QString &name) const +{ + const auto range = m_entries.equal_range(name); + return {range.first, range.second}; } PrimitiveTypeEntryList TypeDatabase::primitiveTypes() const { - TypeEntryHash entries = allEntries(); PrimitiveTypeEntryList returned; - for (TypeEntryHash::const_iterator it = entries.cbegin(), end = entries.cend(); it != end; ++it) { - for (TypeEntry *typeEntry : it.value()) { - if (typeEntry->isPrimitive()) - returned.append(static_cast<PrimitiveTypeEntry *>(typeEntry)); - } + for (auto it = m_entries.cbegin(), end = m_entries.cend(); it != end; ++it) { + TypeEntry *typeEntry = it.value(); + if (typeEntry->isPrimitive()) + returned.append(static_cast<PrimitiveTypeEntry *>(typeEntry)); } return returned; } ContainerTypeEntryList TypeDatabase::containerTypes() const { - TypeEntryHash entries = allEntries(); ContainerTypeEntryList returned; - for (TypeEntryHash::const_iterator it = entries.cbegin(), end = entries.cend(); it != end; ++it) { - for (TypeEntry *typeEntry : it.value()) { - if (typeEntry->isContainer()) - returned.append(static_cast<ContainerTypeEntry *>(typeEntry)); - } + for (auto it = m_entries.cbegin(), end = m_entries.cend(); it != end; ++it) { + TypeEntry *typeEntry = it.value(); + if (typeEntry->isContainer()) + returned.append(static_cast<ContainerTypeEntry *>(typeEntry)); } return returned; } @@ -263,6 +265,8 @@ static inline QString msgRejectReason(const TypeRejection &r, const QString &nee str << " matches class \"" << r.className.pattern() << "\" and \"" << needle << "\" matches \"" << r.pattern.pattern() << '"'; break; + case TypeRejection::Invalid: + break; } return result; } @@ -303,9 +307,52 @@ bool TypeDatabase::isEnumRejected(const QString& className, const QString& enumN return findRejection(m_rejections, TypeRejection::Enum, className, enumName, reason); } -void TypeDatabase::addType(TypeEntry *e) +TypeEntry *TypeDatabase::resolveTypeDefEntry(TypedefEntry *typedefEntry, + QString *errorMessage) +{ + QString sourceName = typedefEntry->sourceType(); + const int lessThanPos = sourceName.indexOf(QLatin1Char('<')); + if (lessThanPos != -1) + sourceName.truncate(lessThanPos); + ComplexTypeEntry *source = nullptr; + for (TypeEntry *e : findTypes(sourceName)) { + switch (e->type()) { + case TypeEntry::BasicValueType: + case TypeEntry::ContainerType: + case TypeEntry::InterfaceType: + case TypeEntry::ObjectType: + case TypeEntry::SmartPointerType: + source = dynamic_cast<ComplexTypeEntry *>(e); + Q_ASSERT(source); + break; + default: + break; + } + } + if (!source) { + if (errorMessage) + *errorMessage = QLatin1String("Unable to resolve typedef \"") + + typedefEntry->sourceType() + QLatin1Char('"'); + return nullptr; + } + + ComplexTypeEntry *result = static_cast<ComplexTypeEntry *>(source->clone()); + result->useAsTypedef(typedefEntry); + typedefEntry->setSource(source); + typedefEntry->setTarget(result); + m_typedefEntries.insert(typedefEntry->qualifiedCppName(), typedefEntry); + return result; +} + +bool TypeDatabase::addType(TypeEntry *e, QString *errorMessage) { - m_entries[e->qualifiedCppName()].append(e); + if (e->type() == TypeEntry::TypedefType) { + e = resolveTypeDefEntry(static_cast<TypedefEntry *>(e), errorMessage); + if (Q_UNLIKELY(!e)) + return false; + } + m_entries.insert(e->qualifiedCppName(), e); + return true; } bool TypeDatabase::isFunctionRejected(const QString& className, const QString& functionName, @@ -339,7 +386,7 @@ FlagsTypeEntry* TypeDatabase::findFlagsType(const QString &name) const fte = m_flagsEntries.value(name); if (!fte) { //last hope, search for flag without scope inside of flags hash - for (SingleTypeEntryHash::const_iterator it = m_flagsEntries.cbegin(), end = m_flagsEntries.cend(); it != end; ++it) { + for (auto it = m_flagsEntries.cbegin(), end = m_flagsEntries.cend(); it != end; ++it) { if (it.key().endsWith(name)) { fte = it.value(); break; @@ -427,12 +474,13 @@ bool TypeDatabase::addSuppressedWarning(const QString &warning, QString *errorMe pattern.append(QLatin1Char('$')); } - const QRegularExpression expression(pattern); + QRegularExpression expression(pattern); if (!expression.isValid()) { *errorMessage = QLatin1String("Invalid message pattern \"") + warning + QLatin1String("\": ") + expression.errorString(); return false; } + expression.setPatternOptions(expression.patternOptions() | QRegularExpression::MultilineOption); m_suppressedWarnings.append(expression); return true; @@ -518,16 +566,21 @@ bool TypeDatabase::parseFile(QIODevice* device, bool generate) { QXmlStreamReader reader(device); Handler handler(this, generate); - return handler.parse(reader); + const bool result = handler.parse(reader); + if (!result) + qCWarning(lcShiboken, "%s", qPrintable(handler.errorString())); + return result; } PrimitiveTypeEntry *TypeDatabase::findPrimitiveType(const QString& name) const { - const TypeEntryList &entries = findTypes(name); - + const auto entries = findTypes(name); for (TypeEntry *entry : entries) { - if (entry && entry->isPrimitive() && static_cast<PrimitiveTypeEntry*>(entry)->preferredTargetLangType()) - return static_cast<PrimitiveTypeEntry*>(entry); + if (entry->isPrimitive()) { + PrimitiveTypeEntry *pe = static_cast<PrimitiveTypeEntry *>(entry); + if (pe->preferredTargetLangType()) + return pe; + } } return 0; @@ -535,9 +588,9 @@ PrimitiveTypeEntry *TypeDatabase::findPrimitiveType(const QString& name) const ComplexTypeEntry* TypeDatabase::findComplexType(const QString& name) const { - const TypeEntryList &entries = findTypes(name); + const auto entries = findTypes(name); for (TypeEntry *entry : entries) { - if (entry && entry->isComplex()) + if (entry->isComplex() && useType(entry)) return static_cast<ComplexTypeEntry*>(entry); } return 0; @@ -545,9 +598,9 @@ ComplexTypeEntry* TypeDatabase::findComplexType(const QString& name) const ObjectTypeEntry* TypeDatabase::findObjectType(const QString& name) const { - const TypeEntryList &entries = findTypes(name); + const auto entries = findTypes(name); for (TypeEntry *entry : entries) { - if (entry && entry->isObject()) + if (entry && entry->isObject() && useType(entry)) return static_cast<ObjectTypeEntry*>(entry); } return 0; @@ -555,9 +608,9 @@ ObjectTypeEntry* TypeDatabase::findObjectType(const QString& name) const NamespaceTypeEntry* TypeDatabase::findNamespaceType(const QString& name) const { - const TypeEntryList &entries = findTypes(name); + const auto entries = findTypes(name); for (TypeEntry *entry : entries) { - if (entry && entry->isNamespace()) + if (entry->isNamespace() && useType(entry)) return static_cast<NamespaceTypeEntry*>(entry); } return 0; @@ -574,75 +627,64 @@ void TypeDatabase::setDropTypeEntries(QStringList dropTypeEntries) m_dropTypeEntries.sort(); } -// Using std::pair to save some memory -// the pair means (revision, typeIndex) -// This global variable exists only because we can't break the ABI -typedef QHash<const TypeEntry*, std::pair<int, int> > TypeRevisionMap; -Q_GLOBAL_STATIC(TypeRevisionMap, typeEntryFields); static bool computeTypeIndexes = true; static int maxTypeIndex; -int getTypeRevision(const TypeEntry* typeEntry) +static bool typeEntryLessThan(const TypeEntry* t1, const TypeEntry* t2) { - return typeEntryFields()->value(typeEntry).first; -} - -void setTypeRevision(TypeEntry* typeEntry, int revision) -{ - (*typeEntryFields())[typeEntry].first = revision; - computeTypeIndexes = true; -} - -static bool compareTypeEntriesByName(const TypeEntry* t1, const TypeEntry* t2) -{ - return t1->qualifiedCppName() < t2->qualifiedCppName(); + if (t1->revision() < t2->revision()) + return true; + return t1->revision() == t2->revision() + && t1->qualifiedCppName() < t2->qualifiedCppName(); } static void _computeTypeIndexes() { TypeDatabase* tdb = TypeDatabase::instance(); - typedef QMap<int, TypeEntryList> GroupedTypeEntries; - GroupedTypeEntries groupedEntries; + + TypeEntryList list; // Group type entries by revision numbers - const TypeEntryHash &allEntries = tdb->allEntries(); - for (TypeEntryHash::const_iterator tit = allEntries.cbegin(), end = allEntries.cend(); tit != end; ++tit) { - for (TypeEntry *entry : tit.value()) { - if (entry->isPrimitive() - || entry->isContainer() - || entry->isFunction() - || !entry->generateCode() - || entry->isEnumValue() - || entry->isVarargs() - || entry->isTypeSystem() - || entry->isVoid() - || entry->isCustom()) - continue; - groupedEntries[getTypeRevision(entry)] << entry; - } - } + const auto &allEntries = tdb->entries(); + list.reserve(allEntries.size()); + for (auto tit = allEntries.cbegin(), end = allEntries.cend(); tit != end; ++tit) { + TypeEntry *entry = tit.value(); + if (entry->isPrimitive() + || entry->isContainer() + || entry->isFunction() + || !entry->generateCode() + || entry->isEnumValue() + || entry->isVarargs() + || entry->isTypeSystem() + || entry->isVoid() + || entry->isCustom()) + continue; + if (!list.contains(entry)) // Remove duplicates + list.append(entry); + } + + // Sort the type entries by revision, name + std::sort(list.begin(), list.end(), typeEntryLessThan); maxTypeIndex = 0; - GroupedTypeEntries::iterator it = groupedEntries.begin(); - for (; it != groupedEntries.end(); ++it) { - // Remove duplicates - TypeEntryList::iterator newEnd = std::unique(it.value().begin(), it.value().end()); - it.value().erase(newEnd, it.value().end()); - // Sort the type entries by name - qSort(it.value().begin(), newEnd, compareTypeEntriesByName); - - for (TypeEntry *entry : qAsConst(it.value())) { - (*typeEntryFields())[entry].second = maxTypeIndex++; - } - } + for (TypeEntry *e : qAsConst(list)) + e->setSbkIndex(maxTypeIndex++); computeTypeIndexes = false; } -int getTypeIndex(const TypeEntry* typeEntry) +void TypeEntry::setRevision(int r) +{ + if (m_revision != r) { + m_revision = r; + computeTypeIndexes = true; + } +} + +int TypeEntry::sbkIndex() const { if (computeTypeIndexes) _computeTypeIndexes(); - return typeEntryFields()->value(typeEntry).second; + return m_sbkIndex; } int getMaxTypeIndex() @@ -652,6 +694,11 @@ int getMaxTypeIndex() return maxTypeIndex; } +void TypeDatabase::clearApiVersions() +{ + apiVersions()->clear(); +} + bool TypeDatabase::setApiVersion(const QString& packageWildcardPattern, const QString &version) { const QString packagePattern = wildcardToRegExp(packageWildcardPattern.trimmed()); @@ -673,9 +720,11 @@ bool TypeDatabase::setApiVersion(const QString& packageWildcardPattern, const QS } bool TypeDatabase::checkApiVersion(const QString &package, - const QVersionNumber &versionNumber) const + const QVersionNumber &versionNumber) { const ApiVersions &versions = *apiVersions(); + if (versions.isEmpty()) // Nothing specified: use latest. + return true; for (int i = 0, size = versions.size(); i < size; ++i) { if (versions.at(i).first.match(package).hasMatch()) return versions.at(i).second >= versionNumber; @@ -684,33 +733,111 @@ bool TypeDatabase::checkApiVersion(const QString &package, } #ifndef QT_NO_DEBUG_STREAM + +#define FORMAT_BOOL(name, var) \ + if (var) \ + d << ", [" << name << ']'; + +#define FORMAT_NONEMPTY_STRING(name, var) \ + if (!var.isEmpty()) \ + d << ", " << name << "=\"" << var << '"'; + +#define FORMAT_LIST_SIZE(name, var) \ + if (!var.isEmpty()) \ + d << ", " << var.size() << ' ' << name; + +template <class Container, class Separator> +static void formatList(QDebug &d, const char *name, const Container &c, Separator sep) +{ + if (const int size = c.size()) { + d << ", " << name << '[' << size << "]=("; + for (int i = 0; i < size; ++i) { + if (i) + d << sep; + d << c.at(i); + } + d << ')'; + } +} + +void TypeEntry::formatDebug(QDebug &d) const +{ + const QString cppName = qualifiedCppName(); + d << '"' << m_name << '"'; + if (m_name != cppName) + d << "\", cppName=\"" << cppName << '"'; + d << ", type=" << m_type << ", codeGeneration=0x" + << hex << m_codeGeneration << dec; + FORMAT_NONEMPTY_STRING("package", m_targetLangPackage) + FORMAT_BOOL("stream", m_stream) + FORMAT_LIST_SIZE("codeSnips", m_codeSnips) + FORMAT_NONEMPTY_STRING("conversionRule", m_conversionRule) + if (!m_version.isNull() && m_version > QVersionNumber(0, 0)) + d << ", version=" << m_version; + if (m_revision) + d << ", revision=" << m_revision; + if (m_sbkIndex) + d << ", sbkIndex=" << m_sbkIndex; + if (m_include.isValid()) + d << ", include=" << m_include; + formatList(d, "extraIncludes", m_extraIncludes, ", "); +} + +void ComplexTypeEntry::formatDebug(QDebug &d) const +{ + TypeEntry::formatDebug(d); + FORMAT_NONEMPTY_STRING("targetLangName", m_targetLangName) + FORMAT_BOOL("QObject", m_qobject) + FORMAT_BOOL("polymorphicBase", m_polymorphicBase) + FORMAT_BOOL("genericClass", m_genericClass) + FORMAT_BOOL("deleteInMainThread", m_deleteInMainThread) + if (m_typeFlags != 0) + d << ", typeFlags=" << m_typeFlags; + d << ", copyableFlag=" << m_copyableFlag + << ", except=" << int(m_exceptionHandling); + FORMAT_NONEMPTY_STRING("defaultSuperclass", m_defaultSuperclass) + FORMAT_NONEMPTY_STRING("polymorphicIdValue", m_polymorphicIdValue) + FORMAT_NONEMPTY_STRING("lookupName", m_lookupName) + FORMAT_NONEMPTY_STRING("targetType", m_targetType) + FORMAT_NONEMPTY_STRING("hash", m_hashFunction) + FORMAT_LIST_SIZE("addedFunctions", m_addedFunctions) + formatList(d, "functionMods", m_functionMods, ", "); + FORMAT_LIST_SIZE("fieldMods", m_fieldMods) +} + +void TypedefEntry::formatDebug(QDebug &d) const +{ + ComplexTypeEntry::formatDebug(d); + d << ", sourceType=\"" << m_sourceType << '"' + << ", source=" << m_source << ", target=" << m_target; +} + +void EnumTypeEntry::formatDebug(QDebug &d) const +{ + TypeEntry::formatDebug(d); + FORMAT_NONEMPTY_STRING("package", m_packageName) + FORMAT_NONEMPTY_STRING("qualifier", m_qualifier) + FORMAT_NONEMPTY_STRING("targetLangName", m_targetLangName) + if (m_flags) + d << ", flags=(" << m_flags << ')'; +} + +void ContainerTypeEntry::formatDebug(QDebug &d) const +{ + ComplexTypeEntry::formatDebug(d); + d << ", type=" << m_type << ",\"" << typeName() << '"'; +} + QDebug operator<<(QDebug d, const TypeEntry *te) { QDebugStateSaver saver(d); d.noquote(); d.nospace(); d << "TypeEntry("; - if (te) { - const QString name = te->name(); - const QString cppName = te->qualifiedCppName(); - d << '"' << name << '"'; - if (name != cppName) - d << "\", cppName=\"" << cppName << '"'; - d << ", type=" << te->type(); - if (te->include().isValid()) - d << ", include=" << te->include(); - const IncludeList &extraIncludes = te->extraIncludes(); - if (const int count = extraIncludes.size()) { - d << ", extraIncludes[" << count << "]="; - for (int i = 0; i < count; ++i) { - if (i) - d << ", "; - d << extraIncludes.at(i); - } - } - } else { + if (te) + te->formatDebug(d); + else d << '0'; - } d << ')'; return d; } @@ -732,25 +859,14 @@ QDebug operator<<(QDebug d, const TemplateEntry *te) void TypeDatabase::formatDebug(QDebug &d) const { - typedef TypeEntryHash::ConstIterator Eit; - typedef SingleTypeEntryHash::ConstIterator Sit; - typedef TemplateEntryHash::ConstIterator TplIt; d << "TypeDatabase(" << "entries[" << m_entries.size() << "]="; - for (Eit it = m_entries.cbegin(), end = m_entries.cend(); it != end; ++it) { - const int count = it.value().size(); - d << '"' << it.key() << "\" [" << count << "]: ("; - for (int t = 0; t < count; ++t) { - if (t) - d << ", "; - d << it.value().at(t); - } - d << ")\n"; - } + for (auto it = m_entries.cbegin(), end = m_entries.cend(); it != end; ++it) + d << " " << it.value() << '\n'; if (!m_templates.isEmpty()) { d << "templates[" << m_templates.size() << "]=("; - const TplIt begin = m_templates.cbegin(); - for (TplIt it = begin, end = m_templates.cend(); it != end; ++it) { + const auto begin = m_templates.cbegin(); + for (auto it = begin, end = m_templates.cend(); it != end; ++it) { if (it != begin) d << ", "; d << it.value(); @@ -759,15 +875,17 @@ void TypeDatabase::formatDebug(QDebug &d) const } if (!m_flagsEntries.isEmpty()) { d << "flags[" << m_flagsEntries.size() << "]=("; - const Sit begin = m_flagsEntries.cbegin(); - for (Sit it = begin, end = m_flagsEntries.cend(); it != end; ++it) { + const auto begin = m_flagsEntries.cbegin(); + for (auto it = begin, end = m_flagsEntries.cend(); it != end; ++it) { if (it != begin) d << ", "; d << it.value(); } d << ")\n"; } - d <<"\nglobalUserFunctions=" << m_globalUserFunctions << ')'; + d <<"\nglobalUserFunctions=" << m_globalUserFunctions << '\n'; + formatList(d, "globalFunctionMods", m_functionMods, '\n'); + d << ')'; } QDebug operator<<(QDebug d, const TypeDatabase &db) diff --git a/sources/shiboken2/ApiExtractor/typedatabase.h b/sources/shiboken2/ApiExtractor/typedatabase.h index 2e7b009c2..247d74362 100644 --- a/sources/shiboken2/ApiExtractor/typedatabase.h +++ b/sources/shiboken2/ApiExtractor/typedatabase.h @@ -54,13 +54,12 @@ struct TypeRejection; QT_FORWARD_DECLARE_CLASS(QDebug) -void setTypeRevision(TypeEntry* typeEntry, int revision); -int getTypeRevision(const TypeEntry* typeEntry); -int getTypeIndex(const TypeEntry* typeEntry); int getMaxTypeIndex(); class ContainerTypeEntry; class PrimitiveTypeEntry; +class TypeSystemTypeEntry; + class TypeDatabase { TypeDatabase(); @@ -91,12 +90,12 @@ public: NamespaceTypeEntry* findNamespaceType(const QString& name) const; ContainerTypeEntry* findContainerType(const QString& name) const; FunctionTypeEntry* findFunctionType(const QString& name) const; + const TypeSystemTypeEntry *findTypeSystemType(const QString &name) const; TypeEntry* findType(const QString& name) const; - TypeEntryHash allEntries() const { return m_entries; } - - SingleTypeEntryHash entries() const; + const TypeEntryMultiMap &entries() const { return m_entries; } + const TypedefEntryMap &typedefEntries() const { return m_typedefEntries; } PrimitiveTypeEntryList primitiveTypes() const; @@ -115,7 +114,7 @@ public: bool isReturnTypeRejected(const QString& className, const QString& typeName, QString *reason = nullptr) const; - void addType(TypeEntry* e); + bool addType(TypeEntry* e, QString *errorMessage = nullptr); FlagsTypeEntry* findFlagsType(const QString& name) const; void addFlagsType(FlagsTypeEntry* fte); @@ -147,9 +146,10 @@ public: bool parseFile(QIODevice* device, bool generate = true); - bool setApiVersion(const QString& package, const QString& version); + static bool setApiVersion(const QString& package, const QString& version); + static void clearApiVersions(); - bool checkApiVersion(const QString &package, const QVersionNumber &version) const; + static bool checkApiVersion(const QString &package, const QVersionNumber &version); bool hasDroppedTypeEntries() const { return !m_dropTypeEntries.isEmpty(); } @@ -163,12 +163,14 @@ public: void formatDebug(QDebug &d) const; #endif private: - TypeEntryList findTypes(const QString &name) const; + TypeEntryMultiMapConstIteratorRange findTypes(const QString &name) const; + TypeEntry *resolveTypeDefEntry(TypedefEntry *typedefEntry, QString *errorMessage); bool m_suppressWarnings; - TypeEntryHash m_entries; - SingleTypeEntryHash m_flagsEntries; - TemplateEntryHash m_templates; + TypeEntryMultiMap m_entries; + TypeEntryMap m_flagsEntries; + TypedefEntryMap m_typedefEntries; + TemplateEntryMap m_templates; QVector<QRegularExpression> m_suppressedWarnings; AddedFunctionList m_globalUserFunctions; diff --git a/sources/shiboken2/ApiExtractor/typedatabase_typedefs.h b/sources/shiboken2/ApiExtractor/typedatabase_typedefs.h index 083602322..fbbbabe43 100644 --- a/sources/shiboken2/ApiExtractor/typedatabase_typedefs.h +++ b/sources/shiboken2/ApiExtractor/typedatabase_typedefs.h @@ -29,7 +29,7 @@ #ifndef TYPEDATABASE_TYPEDEFS_H #define TYPEDATABASE_TYPEDEFS_H -#include <QtCore/QHash> +#include <QtCore/QMultiMap> #include <QtCore/QString> #include <QtCore/QVector> @@ -37,11 +37,28 @@ class ContainerTypeEntry; class PrimitiveTypeEntry; class TemplateEntry; class TypeEntry; +class TypedefEntry; typedef QVector<TypeEntry *> TypeEntryList; -typedef QHash<QString, TypeEntryList> TypeEntryHash; -typedef QHash<QString, TypeEntry *> SingleTypeEntryHash; -typedef QHash<QString, TemplateEntry *> TemplateEntryHash; +typedef QMap<QString, TemplateEntry *> TemplateEntryMap; + +template <class Key, class Value> +struct QMultiMapConstIteratorRange // A range of iterator for a range-based for loop +{ + using ConstIterator = typename QMultiMap<Key, Value>::const_iterator; + + ConstIterator begin() const { return m_begin; } + ConstIterator end() const { return m_end; } + + ConstIterator m_begin; + ConstIterator m_end; +}; + +typedef QMultiMap<QString, TypeEntry *> TypeEntryMultiMap; +typedef QMultiMapConstIteratorRange<QString, TypeEntry *> TypeEntryMultiMapConstIteratorRange; + +typedef QMap<QString, TypeEntry *> TypeEntryMap; +typedef QMap<QString, TypedefEntry *> TypedefEntryMap; typedef QVector<const ContainerTypeEntry *> ContainerTypeEntryList; typedef QVector<const PrimitiveTypeEntry *> PrimitiveTypeEntryList; diff --git a/sources/shiboken2/ApiExtractor/typeparser.cpp b/sources/shiboken2/ApiExtractor/typeparser.cpp index 02c85421b..c440fb66d 100644 --- a/sources/shiboken2/ApiExtractor/typeparser.cpp +++ b/sources/shiboken2/ApiExtractor/typeparser.cpp @@ -49,13 +49,14 @@ public: GreaterThanToken, ConstToken, + VolatileToken, Identifier, NoToken, InvalidToken }; Scanner(const QString &s) - : m_pos(0), m_length(s.length()), m_chars(s.constData()) + : m_pos(0), m_length(s.length()), m_tokenStart(-1), m_chars(s.constData()) { } @@ -137,13 +138,30 @@ Scanner::Token Scanner::nextToken(QString *errorMessage) } } - if (tok == Identifier && m_pos - m_tokenStart == 5) { - if (m_chars[m_tokenStart] == QLatin1Char('c') - && m_chars[m_tokenStart + 1] == QLatin1Char('o') - && m_chars[m_tokenStart + 2] == QLatin1Char('n') - && m_chars[m_tokenStart + 3] == QLatin1Char('s') - && m_chars[m_tokenStart + 4] == QLatin1Char('t')) - tok = ConstToken; + if (tok == Identifier) { + switch (m_pos - m_tokenStart) { + case 5: + if (m_chars[m_tokenStart] == QLatin1Char('c') + && m_chars[m_tokenStart + 1] == QLatin1Char('o') + && m_chars[m_tokenStart + 2] == QLatin1Char('n') + && m_chars[m_tokenStart + 3] == QLatin1Char('s') + && m_chars[m_tokenStart + 4] == QLatin1Char('t')) { + tok = ConstToken; + } + break; + case 8: + if (m_chars[m_tokenStart] == QLatin1Char('v') + && m_chars[m_tokenStart + 1] == QLatin1Char('o') + && m_chars[m_tokenStart + 2] == QLatin1Char('l') + && m_chars[m_tokenStart + 3] == QLatin1Char('a') + && m_chars[m_tokenStart + 4] == QLatin1Char('t') + && m_chars[m_tokenStart + 5] == QLatin1Char('i') + && m_chars[m_tokenStart + 6] == QLatin1Char('l') + && m_chars[m_tokenStart + 7] == QLatin1Char('e')) { + tok = VolatileToken; + } + break; + } } return tok; @@ -167,6 +185,7 @@ TypeInfo TypeParser::parse(const QString &str, QString *errorMessage) bool colon_prefix = false; bool in_array = false; QString array; + bool seenStar = false; Scanner::Token tok = scanner.nextToken(errorMessage); while (tok != Scanner::NoToken) { @@ -191,7 +210,8 @@ TypeInfo TypeParser::parse(const QString &str, QString *errorMessage) switch (tok) { case Scanner::StarToken: - ++stack.top()->m_indirections; + seenStar = true; + stack.top()->addIndirection(Indirection::Pointer); break; case Scanner::AmpersandToken: @@ -212,14 +232,14 @@ TypeInfo TypeParser::parse(const QString &str, QString *errorMessage) } break; case Scanner::LessThanToken: - stack.top()->m_arguments << TypeInfo(); - stack.push(&stack.top()->m_arguments.last()); + stack.top()->m_instantiations << TypeInfo(); + stack.push(&stack.top()->m_instantiations.last()); break; case Scanner::CommaToken: stack.pop(); - stack.top()->m_arguments << TypeInfo(); - stack.push(&stack.top()->m_arguments.last()); + stack.top()->m_instantiations << TypeInfo(); + stack.push(&stack.top()->m_instantiations.last()); break; case Scanner::GreaterThanToken: @@ -231,7 +251,16 @@ TypeInfo TypeParser::parse(const QString &str, QString *errorMessage) break; case Scanner::ConstToken: - stack.top()->m_constant = true; + if (seenStar) { // "int *const": Last indirection is const. + Q_ASSERT(!stack.top()->m_indirections.isEmpty()); + *stack.top()->m_indirections.rbegin() = Indirection::ConstPointer; + } else { + stack.top()->m_constant = true; + } + break; + + case Scanner::VolatileToken: + stack.top()->m_volatile = true; break; case Scanner::OpenParenToken: // function pointers not supported diff --git a/sources/shiboken2/ApiExtractor/typesystem.cpp b/sources/shiboken2/ApiExtractor/typesystem.cpp index 8e0e4437a..e82221a40 100644 --- a/sources/shiboken2/ApiExtractor/typesystem.cpp +++ b/sources/shiboken2/ApiExtractor/typesystem.cpp @@ -29,35 +29,75 @@ #include "typesystem.h" #include "typesystem_p.h" #include "typedatabase.h" +#include "messages.h" #include "reporthandler.h" #include <QtCore/QDir> #include <QtCore/QFile> #include <QtCore/QFileInfo> #include <QtCore/QRegularExpression> #include <QtCore/QSet> +#include <QtCore/QStringView> +#include <QtCore/QStringAlgorithms> #include <QtCore/QXmlStreamAttributes> #include <QtCore/QXmlStreamReader> +#include <algorithm> + +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 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 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"); } @@ -90,105 +130,317 @@ static bool setRejectionRegularExpression(const QString &patternIn, return true; } -static bool addRejection(TypeDatabase *database, const QHash<QString, QString> &attributes, - QString *errorMessage) +// Extract a snippet from a file within annotation "// @snippet label". +static QString extractSnippet(const QString &code, const QString &snippetLabel) { - typedef QPair<QString, TypeRejection::MatchType> AttributeMatchTypePair; + 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()); - TypeRejection rejection; + 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; +} - const QString className = attributes.value(classAttribute()); - if (!setRejectionRegularExpression(className, &rejection.className, errorMessage)) - return false; +template <class EnumType, Qt::CaseSensitivity cs = Qt::CaseInsensitive> +struct EnumLookup +{ + QStringView name; + EnumType value; +}; - static const AttributeMatchTypePair attributeMatchTypeMapping[] = - {{functionNameAttribute(), TypeRejection::Function}, - {fieldNameAttribute(), TypeRejection::Field}, - {enumNameAttribute(), TypeRejection::Enum}, - {argumentTypeAttribute(), TypeRejection::ArgumentType}, - {returnTypeAttribute(), TypeRejection::ReturnType} +template <class EnumType, Qt::CaseSensitivity cs> +bool operator==(const EnumLookup<EnumType, cs> &e1, const EnumLookup<EnumType, cs> &e2) +{ + return e1.name.compare(e2.name, cs) == 0; +} + +template <class EnumType, Qt::CaseSensitivity cs> +bool operator<(const EnumLookup<EnumType, cs> &e1, const EnumLookup<EnumType, cs> &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<EnumType, caseSensitivity> 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() - // 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; - } - } +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() - // 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; +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; } - rejection.matchType = TypeRejection::ExcludeClass; - database->addRejection(rejection); - return true; + return -1; } +static QString msgMissingAttribute(const QString &a) +{ + return QLatin1String("Required attribute '") + a + + QLatin1String("' missing."); +} -Handler::Handler(TypeDatabase* database, bool generate) - : m_database(database), m_generate(generate ? TypeEntry::GenerateAll : TypeEntry::GenerateForSubclass) -{ - m_currentEnum = 0; - m_current = 0; - m_currentDroppedEntry = 0; - m_currentDroppedEntryDepth = 0; - m_ignoreDepth = 0; - - tagNames.insert(QLatin1String("rejection"), StackElement::Rejection); - tagNames.insert(QLatin1String("custom-type"), StackElement::CustomTypeEntry); - tagNames.insert(QLatin1String("primitive-type"), StackElement::PrimitiveTypeEntry); - tagNames.insert(QLatin1String("container-type"), StackElement::ContainerTypeEntry); - tagNames.insert(QLatin1String("object-type"), StackElement::ObjectTypeEntry); - tagNames.insert(QLatin1String("value-type"), StackElement::ValueTypeEntry); - tagNames.insert(QLatin1String("interface-type"), StackElement::InterfaceTypeEntry); - tagNames.insert(QLatin1String("namespace-type"), StackElement::NamespaceTypeEntry); - tagNames.insert(QLatin1String("enum-type"), StackElement::EnumTypeEntry); - tagNames.insert(QLatin1String("smart-pointer-type"), StackElement::SmartPointerTypeEntry); - tagNames.insert(QLatin1String("function"), StackElement::FunctionTypeEntry); - tagNames.insert(QLatin1String("extra-includes"), StackElement::ExtraIncludes); - tagNames.insert(QLatin1String("include"), StackElement::Include); - tagNames.insert(QLatin1String("inject-code"), StackElement::InjectCode); - tagNames.insert(QLatin1String("modify-function"), StackElement::ModifyFunction); - tagNames.insert(QLatin1String("modify-field"), StackElement::ModifyField); - tagNames.insert(QLatin1String("access"), StackElement::Access); - tagNames.insert(QLatin1String("remove"), StackElement::Removal); - tagNames.insert(QLatin1String("rename"), StackElement::Rename); - tagNames.insert(QLatin1String("typesystem"), StackElement::Root); - tagNames.insert(QLatin1String("custom-constructor"), StackElement::CustomMetaConstructor); - tagNames.insert(QLatin1String("custom-destructor"), StackElement::CustomMetaDestructor); - tagNames.insert(QLatin1String("argument-map"), StackElement::ArgumentMap); - tagNames.insert(QLatin1String("suppress-warning"), StackElement::SuppressedWarning); - tagNames.insert(QLatin1String("load-typesystem"), StackElement::LoadTypesystem); - tagNames.insert(QLatin1String("define-ownership"), StackElement::DefineOwnership); - tagNames.insert(QLatin1String("replace-default-expression"), StackElement::ReplaceDefaultExpression); - tagNames.insert(QLatin1String("reject-enum-value"), StackElement::RejectEnumValue); - tagNames.insert(QLatin1String("replace-type"), StackElement::ReplaceType); - tagNames.insert(QLatin1String("conversion-rule"), StackElement::ConversionRule); - tagNames.insert(QLatin1String("native-to-target"), StackElement::NativeToTarget); - tagNames.insert(QLatin1String("target-to-native"), StackElement::TargetToNative); - tagNames.insert(QLatin1String("add-conversion"), StackElement::AddConversion); - tagNames.insert(QLatin1String("modify-argument"), StackElement::ModifyArgument); - tagNames.insert(QLatin1String("remove-argument"), StackElement::RemoveArgument); - tagNames.insert(QLatin1String("remove-default-expression"), StackElement::RemoveDefaultExpression); - tagNames.insert(QLatin1String("template"), StackElement::Template); - tagNames.insert(QLatin1String("insert-template"), StackElement::TemplateInstanceEnum); - tagNames.insert(QLatin1String("replace"), StackElement::Replace); - 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); +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; +} + +Handler::Handler(TypeDatabase *database, bool generate) : + m_database(database), + m_generate(generate ? TypeEntry::GenerateAll : TypeEntry::GenerateForSubclass) +{ } static QString readerFileName(const QXmlStreamReader &reader) @@ -197,22 +449,72 @@ static QString readerFileName(const QXmlStreamReader &reader) return file != nullptr ? file->fileName() : QString(); } -static QString msgReaderError(const QXmlStreamReader &reader, const QString &what) +static QString msgReaderMessage(const QXmlStreamReader &reader, + const char *type, + const QString &what) { QString message; QTextStream str(&message); - str << "Error: "; + str << type << ": "; const QString fileName = readerFileName(reader); - if (!fileName.isEmpty()) - str << "file=" << QDir::toNativeSeparators(fileName) << ", "; - str << "line=" << reader.lineNumber() << ", column=" << reader.columnNumber() - << ", message=" << what; + if (fileName.isEmpty()) + str << "<stdin>:"; + else + str << QDir::toNativeSeparators(fileName) << ':'; + str << reader.lineNumber() << ':' << reader.columnNumber() + << ": " << what; return message; } -static QString msgReaderError(const QXmlStreamReader &reader) +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 msgReaderError(reader, reader.errorString()); + return msgUnimplementedAttributeValueWarning(reader, + attribute.qualifiedName(), + attribute.value()); } static QString msgInvalidVersion(const QStringRef &version, const QString &package = QString()) @@ -226,6 +528,53 @@ static QString msgInvalidVersion(const QStringRef &version, const QString &packa 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(); @@ -238,10 +587,10 @@ bool Handler::parse(QXmlStreamReader &reader) switch (reader.readNext()) { case QXmlStreamReader::NoToken: case QXmlStreamReader::Invalid: - qCWarning(lcShiboken).noquote().nospace() << msgReaderError(reader); + m_error = msgReaderError(reader, reader.errorString()); return false; case QXmlStreamReader::StartElement: - if (!startElement(reader.name(), reader.attributes())) { + if (!startElement(reader)) { m_error = msgReaderError(reader, m_error); return false; } @@ -271,22 +620,6 @@ bool Handler::parse(QXmlStreamReader &reader) return true; } -void Handler::fetchAttributeValues(const QString &name, const QXmlStreamAttributes &atts, - QHash<QString, QString> *acceptedAttributes) -{ - Q_ASSERT(acceptedAttributes); - - for (int i = 0; i < atts.length(); ++i) { - const QString key = atts.at(i).name().toString().toLower(); - if (!acceptedAttributes->contains(key)) { - qCWarning(lcShiboken).noquote().nospace() - << QStringLiteral("Unknown attribute for '%1': '%2'").arg(name, key); - } else { - acceptedAttributes->insert(key, atts.at(i).value().toString()); - } - } -} - bool Handler::endElement(const QStringRef &localName) { if (m_ignoreDepth) { @@ -416,14 +749,18 @@ bool Handler::endElement(const QStringRef &localName) break; } - if (m_current->type == StackElement::Root - || m_current->type == StackElement::NamespaceTypeEntry - || m_current->type == StackElement::InterfaceTypeEntry - || m_current->type == StackElement::ObjectTypeEntry - || m_current->type == StackElement::ValueTypeEntry - || m_current->type == StackElement::PrimitiveTypeEntry) { - StackElementContext* context = m_contextStack.pop(); - delete context; + 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; @@ -551,8 +888,9 @@ bool Handler::importFileElement(const QXmlStreamAttributes &atts) return true; } -static bool convertBoolean(const QString &value, const QString &attributeName, bool defaultValue) +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; @@ -561,28 +899,47 @@ static bool convertBoolean(const QString &value, const QString &attributeName, b || 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, attributeName, + .arg(value) + .arg(attributeName, defaultValue ? yesAttributeValue() : noAttributeValue()); qCWarning(lcShiboken).noquote().nospace() << warn; return defaultValue; } -static bool convertRemovalAttribute(const QString& removalAttribute, Modification& mod, QString& errorMsg) +static bool convertRemovalAttribute(QStringView remove, Modification& mod, QString& errorMsg) { - QString remove = removalAttribute.toLower(); - if (!remove.isEmpty()) { - if (remove == QLatin1String("all")) { - mod.removal = TypeSystem::All; - } else if (remove == QLatin1String("target")) { - mod.removal = TypeSystem::TargetLangAndNativeCode; - } else { - errorMsg = QString::fromLatin1("Bad removal type '%1'").arg(remove); - return false; - } + 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; } - 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) @@ -615,98 +972,1466 @@ static QString checkSignatureError(const QString& signature, const QString& tag) return QString(); } -void Handler::addFlags(const QString &name, QString flagName, - const QHash<QString, QString> &attributes, - const QVersionNumber &since) +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(m_currentEnum); + 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); - ftype->setCodeGeneration(m_generate); + applyCommonAttributes(ftype, attributes); QString n = ftype->originalName(); QStringList lst = n.split(colonColon()); - if (QStringList(lst.mid(0, lst.size() - 1)).join(colonColon()) != m_currentEnum->targetLangQualifier()) { + 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(m_currentEnum->targetLangQualifier(), lst.constFirst()); + .arg(targetLangQualifier, lst.constFirst()); } ftype->setFlagsName(lst.constLast()); - m_currentEnum->setFlags(ftype); + enumEntry->setFlags(ftype); m_database->addFlagsType(ftype); m_database->addType(ftype); - QString revision = attributes.value(QLatin1String("flags-revision")); - if (revision.isEmpty()) - revision = attributes.value(QLatin1String("revision")); - setTypeRevision(ftype, revision.toInt()); -} + 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(); + } + } -bool Handler::handleSmartPointerEntry(StackElement *element, - QHash<QString, QString> &attributes, - const QString &name, - const QVersionNumber &since) -{ - QString smartPointerType = attributes[QLatin1String("type")]; if (smartPointerType.isEmpty()) { m_error = QLatin1String("No type specified for the smart pointer. Currently supported types: 'shared',"); - return false; + return nullptr; } if (smartPointerType != QLatin1String("shared")) { m_error = QLatin1String("Currently only the 'shared' type is supported."); - return false; + return nullptr; } - QString getter = attributes[QLatin1String("getter")]; if (getter.isEmpty()) { m_error = QLatin1String("No function getter name specified for getting the raw pointer held by the smart pointer."); - return false; + return nullptr; } - QString refCountMethodName = attributes[QLatin1String("ref-count-method")]; QString signature = getter + QLatin1String("()"); - signature = TypeDatabase::normalizedSignature(signature); if (signature.isEmpty()) { m_error = QLatin1String("No signature for the smart pointer getter found."); - return false; + return nullptr; } QString errorString = checkSignatureError(signature, QLatin1String("smart-pointer-type")); if (!errorString.isEmpty()) { m_error = errorString; - return false; + return nullptr; + } + + SmartPointerTypeEntry *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) +{ + PrimitiveTypeEntry *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()); + } } - SmartPointerTypeEntry *type = new SmartPointerTypeEntry(name, - getter, - smartPointerType, - refCountMethodName, - since); + if (type->targetLangName().isEmpty()) + type->setTargetLangName(type->name()); + if (type->targetLangApiName().isEmpty()) + type->setTargetLangApiName(type->name()); type->setTargetLangPackage(m_defaultPackage); - type->setCodeGeneration(m_generate); - element->entry = type; + 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; + } + ContainerTypeEntry *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); + } + EnumTypeEntry *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) +{ + ObjectTypeEntry *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; +} + +ValueTypeEntry * + Handler::parseValueTypeEntry(const QXmlStreamReader &, + const QString &name, const QVersionNumber &since, + QXmlStreamAttributes *attributes) +{ + ValueTypeEntry *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) { + FunctionTypeEntry *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; + } + + FunctionTypeEntry *result = reinterpret_cast<FunctionTypeEntry *>(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; + + 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 == 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); + + // 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))); + } + } + } + + TypeSystemTypeEntry *moduleEntry = + const_cast<TypeSystemTypeEntry *>(m_database->findTypeSystemType(m_defaultPackage)); + if (!moduleEntry) + 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 (!moduleEntry->qualifiedCppName().isEmpty()) + m_database->addType(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("<reject-enum-value> node must be used inside a <enum-type> 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::startElement(const QStringRef &n, const QXmlStreamAttributes &atts) +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; + } + } + } + + CustomConversion* 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; + } + + AddedFunction func(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; + + 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(); + } + CustomFunction *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); - const QStringRef sinceSpec = atts.value(sinceAttribute()); - if (!sinceSpec.isNull()) { + 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); @@ -722,12 +2447,11 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts } } - const QString tagName = n.toString().toLower(); - if (tagName == QLatin1String("import-file")) - return importFileElement(atts); + if (tagName.compare(QLatin1String("import-file"), Qt::CaseInsensitive) == 0) + return importFileElement(attributes); - const QHash<QString, StackElement::ElementType>::const_iterator tit = tagNames.constFind(tagName); - if (tit == tagNames.constEnd()) { + const StackElement::ElementType elementType = elementFromTag(tagName); + if (elementType == StackElement::None) { m_error = QStringLiteral("Unknown tag name: '%1'").arg(tagName); return false; } @@ -738,92 +2462,48 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts } StackElement* element = new StackElement(m_current); - element->type = tit.value(); + element->type = elementType; if (element->type == StackElement::Root && m_generate == TypeEntry::GenerateAll) customConversionsForReview.clear(); - if (element->type == StackElement::Root - || element->type == StackElement::NamespaceTypeEntry - || element->type == StackElement::InterfaceTypeEntry - || element->type == StackElement::ObjectTypeEntry - || element->type == StackElement::ValueTypeEntry - || element->type == StackElement::PrimitiveTypeEntry) { + 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) { - QHash<QString, QString> attributes; - attributes.insert(nameAttribute(), QString()); - attributes.insert(QLatin1String("revision"), QLatin1String("0")); - attributes.insert(sinceAttribute(), QString()); // dummy for matching allowed attributes - - switch (element->type) { - case StackElement::PrimitiveTypeEntry: - attributes.insert(QLatin1String("target-lang-name"), QString()); - attributes.insert(QLatin1String("target-lang-api-name"), QString()); - attributes.insert(QLatin1String("preferred-conversion"), yesAttributeValue()); - attributes.insert(QLatin1String("preferred-target-lang-type"), yesAttributeValue()); - attributes.insert(QLatin1String("default-constructor"), QString()); - break; - case StackElement::ContainerTypeEntry: - attributes.insert(QLatin1String("type"), QString()); - break; - case StackElement::SmartPointerTypeEntry: - attributes.insert(QLatin1String("type"), QString()); - attributes.insert(QLatin1String("getter"), QString()); - attributes.insert(QLatin1String("ref-count-method"), QString()); - break; - case StackElement::EnumTypeEntry: - attributes.insert(flagsAttribute(), QString()); - attributes.insert(QLatin1String("flags-revision"), QString()); - attributes.insert(QLatin1String("upper-bound"), QString()); - attributes.insert(QLatin1String("lower-bound"), QString()); - attributes.insert(QLatin1String("force-integer"), noAttributeValue()); - attributes.insert(QLatin1String("extensible"), noAttributeValue()); - attributes.insert(enumIdentifiedByValueAttribute(), QString()); - attributes.insert(classAttribute(), falseAttributeValue()); - break; - case StackElement::ValueTypeEntry: - attributes.insert(QLatin1String("default-constructor"), QString()); - Q_FALLTHROUGH(); - case StackElement::ObjectTypeEntry: - attributes.insert(QLatin1String("force-abstract"), noAttributeValue()); - attributes.insert(QLatin1String("deprecated"), noAttributeValue()); - attributes.insert(QLatin1String("hash-function"), QString()); - attributes.insert(QLatin1String("stream"), noAttributeValue()); - 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"), noAttributeValue()); - attributes.insert(QLatin1String("held-type"), QString()); - attributes.insert(QLatin1String("copyable"), QString()); - Q_FALLTHROUGH(); - case StackElement::NamespaceTypeEntry: - attributes.insert(QLatin1String("target-lang-name"), QString()); - attributes[QLatin1String("package")] = m_defaultPackage; - attributes.insert(QLatin1String("expense-cost"), QLatin1String("1")); - attributes.insert(QLatin1String("expense-limit"), QLatin1String("none")); - attributes.insert(QLatin1String("polymorphic-base"), noAttributeValue()); - attributes.insert(QLatin1String("generate"), yesAttributeValue()); - attributes.insert(QLatin1String("target-type"), QString()); - attributes.insert(QLatin1String("generic-class"), noAttributeValue()); - break; - case StackElement::FunctionTypeEntry: - attributes.insert(QLatin1String("signature"), QString()); - attributes.insert(QLatin1String("rename"), QString()); - break; - default: - { } // nada - }; - - fetchAttributeValues(tagName, atts, &attributes); - QString name = attributes[nameAttribute()]; + 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[QLatin1String("signature")] : name); + identifier += element->type == StackElement::FunctionTypeEntry + ? attributes.value(signatureAttribute()).toString() + : name; if (m_database->shouldDropTypeEntry(identifier)) { m_currentDroppedEntry = element; m_currentDroppedEntryDepth = 1; @@ -837,30 +2517,9 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts // The top level tag 'function' has only the 'signature' tag // and we should extract the 'name' value from it. - if (element->type == StackElement::FunctionTypeEntry) { - QString signature = attributes[QLatin1String("signature")]; - name = signature.left(signature.indexOf(QLatin1Char('('))).trimmed(); - QString errorString = checkSignatureError(signature, QLatin1String("function")); - if (!errorString.isEmpty()) { - m_error = errorString; + if (element->type == StackElement::FunctionTypeEntry + && !parseRenameFunction(reader, &name, &attributes)) { return false; - } - QString rename = attributes[QLatin1String("rename")]; - 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 = attributes[QLatin1String("rename")]; - mod.modifiers |= Modification::Rename; - m_contextStack.top()->functionMods << mod; - } } // We need to be able to have duplicate primitive type entries, @@ -875,7 +2534,9 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts } if (element->type == StackElement::EnumTypeEntry) { - const QString identifiedByValue = attributes.value(enumIdentifiedByValueAttribute()); + 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()) { @@ -900,281 +2561,92 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts case StackElement::CustomTypeEntry: element->entry = new TypeEntry(name, TypeEntry::CustomType, since); break; - case StackElement::PrimitiveTypeEntry: { - QString targetLangName = attributes[QLatin1String("target-lang-name")]; - QString targetLangApiName = attributes[QLatin1String("target-lang-api-name")]; - QString preferredConversion = attributes[QLatin1String("preferred-conversion")].toLower(); - QString preferredTargetLangType = attributes[QLatin1String("preferred-target-lang-type")].toLower(); - QString defaultConstructor = attributes[QLatin1String("default-constructor")]; - - if (targetLangName.isEmpty()) - targetLangName = name; - if (targetLangApiName.isEmpty()) - targetLangApiName = name; - - PrimitiveTypeEntry *type = new PrimitiveTypeEntry(name, since); - type->setCodeGeneration(m_generate); - type->setTargetLangName(targetLangName); - type->setTargetLangApiName(targetLangApiName); - type->setTargetLangPackage(m_defaultPackage); - type->setDefaultConstructor(defaultConstructor); - - bool preferred; - preferred = convertBoolean(preferredConversion, QLatin1String("preferred-conversion"), true); - type->setPreferredConversion(preferred); - preferred = convertBoolean(preferredTargetLangType, - QLatin1String("preferred-target-lang-type"), true); - type->setPreferredTargetLangType(preferred); - - element->entry = type; - } - break; - - case StackElement::ContainerTypeEntry: { - QString typeName = attributes[QLatin1String("type")]; - ContainerTypeEntry::Type containerType = - ContainerTypeEntry::containerTypeFromString(typeName); - if (typeName.isEmpty()) { - m_error = QLatin1String("no 'type' attribute specified"); + case StackElement::PrimitiveTypeEntry: + element->entry = parsePrimitiveTypeEntry(reader, name, since, &attributes); + if (Q_UNLIKELY(!element->entry)) return false; - } else if (containerType == ContainerTypeEntry::NoContainer) { - m_error = QLatin1String("there is no container of type ") + typeName; + break; + case StackElement::ContainerTypeEntry: + if (ContainerTypeEntry *ce = parseContainerTypeEntry(reader, name, since, &attributes)) { + applyComplexTypeAttributes(reader, ce, &attributes); + element->entry = ce; + } else { return false; } + break; - ContainerTypeEntry *type = new ContainerTypeEntry(name, containerType, since); - type->setCodeGeneration(m_generate); - element->entry = type; - } - break; - - case StackElement::SmartPointerTypeEntry: { - bool result = handleSmartPointerEntry(element, attributes, name, since); - if (!result) - return result; - } - break; - - case StackElement::EnumTypeEntry: { - QStringList names = name.split(colonColon()); - if (names.size() == 1) - m_currentEnum = new EnumTypeEntry(QString(), name, since); - else - m_currentEnum = - new EnumTypeEntry(QStringList(names.mid(0, names.size() - 1)).join(colonColon()), - names.constLast(), since); - element->entry = m_currentEnum; - m_currentEnum->setCodeGeneration(m_generate); - m_currentEnum->setTargetLangPackage(m_defaultPackage); - m_currentEnum->setUpperBound(attributes[QLatin1String("upper-bound")]); - m_currentEnum->setLowerBound(attributes[QLatin1String("lower-bound")]); - m_currentEnum->setForceInteger(convertBoolean(attributes[QLatin1String("force-integer")], QLatin1String("force-integer"), false)); - m_currentEnum->setExtensible(convertBoolean(attributes[QLatin1String("extensible")], QLatin1String("extensible"), false)); - - // put in the flags parallel... - const QString flagNames = attributes.value(flagsAttribute()); - if (!flagNames.isEmpty()) { - const QStringList &flagNameList = flagNames.split(QLatin1Char(',')); - for (const QString &flagName : flagNameList) - addFlags(name, flagName.trimmed(), attributes, since); + case StackElement::SmartPointerTypeEntry: + if (SmartPointerTypeEntry *se = parseSmartPointerEntry(reader, name, since, &attributes)) { + applyComplexTypeAttributes(reader, se, &attributes); + element->entry = se; + } else { + return false; } - } - break; + 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: { - ObjectTypeEntry *otype = new ObjectTypeEntry(name, since); - QString targetLangName = attributes[QLatin1String("target-lang-name")]; - if (targetLangName.isEmpty()) - targetLangName = name; - InterfaceTypeEntry *itype = - new InterfaceTypeEntry(InterfaceTypeEntry::interfaceName(targetLangName), since); - - if (!convertBoolean(attributes[QLatin1String("generate")], QLatin1String("generate"), true)) - itype->setCodeGeneration(TypeEntry::GenerateForSubclass); - else - itype->setCodeGeneration(m_generate); - otype->setDesignatedInterface(itype); - itype->setOrigin(otype); - element->entry = otype; - } - Q_FALLTHROUGH(); - case StackElement::ValueTypeEntry: { - if (!element->entry) { - ValueTypeEntry* typeEntry = new ValueTypeEntry(name, since); - QString defaultConstructor = attributes[QLatin1String("default-constructor")]; - if (!defaultConstructor.isEmpty()) - typeEntry->setDefaultConstructor(defaultConstructor); - element->entry = typeEntry; + case StackElement::InterfaceTypeEntry: + if (ObjectTypeEntry *oe = parseInterfaceTypeEntry(reader, name, since, &attributes)) { + applyComplexTypeAttributes(reader, oe, &attributes); + element->entry = oe; + } else { + return false; } - - Q_FALLTHROUGH(); + 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 (!element->entry) - element->entry = new NamespaceTypeEntry(name, since); - - Q_FALLTHROUGH(); + element->entry = new NamespaceTypeEntry(name, since); + applyCommonAttributes(element->entry, &attributes); + applyComplexTypeAttributes(reader, static_cast<ComplexTypeEntry *>(element->entry), &attributes); + break; case StackElement::ObjectTypeEntry: - if (!element->entry) - element->entry = new ObjectTypeEntry(name, since); - - element->entry->setStream(attributes[QLatin1String("stream")] == yesAttributeValue()); - - ComplexTypeEntry *ctype = static_cast<ComplexTypeEntry *>(element->entry); - ctype->setTargetLangPackage(attributes[QLatin1String("package")]); - ctype->setDefaultSuperclass(attributes[QLatin1String("default-superclass")]); - ctype->setGenericClass(convertBoolean(attributes[QLatin1String("generic-class")], QLatin1String("generic-class"), false)); - - if (!convertBoolean(attributes[QLatin1String("generate")], QLatin1String("generate"), true)) - element->entry->setCodeGeneration(TypeEntry::GenerateForSubclass); - else - element->entry->setCodeGeneration(m_generate); - - QString targetLangName = attributes[QLatin1String("target-lang-name")]; - if (!targetLangName.isEmpty()) - ctype->setTargetLangName(targetLangName); - - ctype->setIsPolymorphicBase(convertBoolean(attributes[QLatin1String("polymorphic-base")], QLatin1String("polymorphic-base"), false)); - ctype->setPolymorphicIdValue(attributes[QLatin1String("polymorphic-id-expression")]); - //Copyable - if (attributes[QLatin1String("copyable")].isEmpty()) - ctype->setCopyable(ComplexTypeEntry::Unknown); - else { - if (convertBoolean(attributes[QLatin1String("copyable")], QLatin1String("copyable"), false)) - ctype->setCopyable(ComplexTypeEntry::CopyableSet); - else - ctype->setCopyable(ComplexTypeEntry::NonCopyableSet); - - } - - if (element->type == StackElement::ObjectTypeEntry || element->type == StackElement::ValueTypeEntry) - ctype->setHashFunction(attributes[QLatin1String("hash-function")]); - - - ctype->setHeldType(attributes[QLatin1String("held-type")]); - - if (element->type == StackElement::ObjectTypeEntry - || element->type == StackElement::ValueTypeEntry) { - if (convertBoolean(attributes[QLatin1String("force-abstract")], QLatin1String("force-abstract"), false)) - ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::ForceAbstract); - if (convertBoolean(attributes[QLatin1String("deprecated")], QLatin1String("deprecated"), false)) - ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::Deprecated); - } - - if (element->type == StackElement::InterfaceTypeEntry - || element->type == StackElement::ValueTypeEntry - || element->type == StackElement::ObjectTypeEntry) { - if (convertBoolean(attributes[QLatin1String("delete-in-main-thread")], QLatin1String("delete-in-main-thread"), false)) - ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::DeleteInMainThread); - } - - QString targetType = attributes[QLatin1String("target-type")]; - if (!targetType.isEmpty() && element->entry->isComplex()) - static_cast<ComplexTypeEntry *>(element->entry)->setTargetType(targetType); - - // ctype->setInclude(Include(Include::IncludePath, ctype->name())); - ctype = ctype->designatedInterface(); - if (ctype) - ctype->setTargetLangPackage(attributes[QLatin1String("package")]); - - } - break; - case StackElement::FunctionTypeEntry: { - QString signature = attributes[QLatin1String("signature")]; - signature = TypeDatabase::normalizedSignature(signature); - element->entry = m_database->findType(name); - if (element->entry) { - if (element->entry->type() == TypeEntry::FunctionType) { - reinterpret_cast<FunctionTypeEntry*>(element->entry)->addSignature(signature); - } else { - 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 false; - } + element->entry = new ObjectTypeEntry(name, since); + applyCommonAttributes(element->entry, &attributes); + applyComplexTypeAttributes(reader, static_cast<ComplexTypeEntry *>(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 { - element->entry = new FunctionTypeEntry(name, signature, since); - element->entry->setCodeGeneration(m_generate); + return false; } - } - break; + break; default: Q_ASSERT(false); }; if (element->entry) { - m_database->addType(element->entry); - setTypeRevision(element->entry, attributes[QLatin1String("revision")].toInt()); + 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) { - // check the XML tag attributes - QHash<QString, QString> attributes; - attributes.insert(QLatin1String("mode"), QLatin1String("replace")); - attributes.insert(QLatin1String("format"), QLatin1String("native")); - attributes.insert(sinceAttribute(), QString()); // dummy for matching allowed attributes - - fetchAttributeValues(tagName, atts, &attributes); - - const int validParent = StackElement::TypeEntryMask - | StackElement::ModifyFunction - | StackElement::ModifyField; - if (m_current->parent && m_current->parent->type & validParent) { - QString modeName = attributes[QLatin1String("mode")]; - TypeSystem::DocModificationMode mode; - if (modeName == QLatin1String("append")) { - mode = TypeSystem::DocModificationAppend; - } else if (modeName == QLatin1String("prepend")) { - mode = TypeSystem::DocModificationPrepend; - } else if (modeName == QLatin1String("replace")) { - mode = TypeSystem::DocModificationReplace; - } else { - m_error = QLatin1String("Unknow documentation injection mode: ") + modeName; - return false; - } - - static QHash<QString, TypeSystem::Language> languageNames; - if (languageNames.isEmpty()) { - languageNames[QLatin1String("target")] = TypeSystem::TargetLangCode; - languageNames[QLatin1String("native")] = TypeSystem::NativeCode; - } - - QString format = attributes[QLatin1String("format")].toLower(); - TypeSystem::Language lang = languageNames.value(format, TypeSystem::NoLanguage); - if (lang == TypeSystem::NoLanguage) { - 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; - } else { - m_error = QLatin1String("inject-documentation must be inside modify-function, " - "modify-field or other tags that creates a type"); + if (!parseInjectDocumentation(reader, &attributes)) return false; - } } else if (element->type == StackElement::ModifyDocumentation) { - // check the XML tag attributes - QHash<QString, QString> attributes; - attributes.insert(xPathAttribute(), QString()); - attributes.insert(sinceAttribute(), QString()); // dummy for matching allowed attributes - fetchAttributeValues(tagName, atts, &attributes); - - 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.value(xPathAttribute()), signature); - } else { - m_error = QLatin1String("modify-documentation must be inside modify-function, " - "modify-field or other tags that creates a type"); + if (!parseModifyDocumentation(reader, &attributes)) return false; - } } else if (element->type != StackElement::None) { bool topLevel = element->type == StackElement::Root || element->type == StackElement::SuppressedWarning @@ -1194,494 +2666,86 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts StackElement topElement = !m_current ? StackElement(0) : *m_current; element->entry = topElement.entry; - QHash<QString, QString> attributes; - attributes.insert(sinceAttribute(), QString()); // dummy for matching allowed attributes switch (element->type) { case StackElement::Root: - attributes.insert(QLatin1String("package"), QString()); - attributes.insert(QLatin1String("default-superclass"), QString()); + element->entry = parseRootElement(reader, since, &attributes); + element->type = StackElement::Root; break; case StackElement::LoadTypesystem: - attributes.insert(nameAttribute(), QString()); - attributes.insert(QLatin1String("generate"), yesAttributeValue()); - break; - case StackElement::NoNullPointers: - attributes.insert(QLatin1String("default-value"), QString()); - break; - case StackElement::SuppressedWarning: - attributes.insert(textAttribute(), QString()); - break; - case StackElement::ReplaceDefaultExpression: - attributes.insert(QLatin1String("with"), QString()); - break; - case StackElement::DefineOwnership: - attributes.insert(QLatin1String("class"), QLatin1String("target")); - attributes.insert(QLatin1String("owner"), QString()); - break; - case StackElement::AddFunction: - attributes.insert(QLatin1String("signature"), QString()); - attributes.insert(QLatin1String("return-type"), QLatin1String("void")); - attributes.insert(QLatin1String("access"), QLatin1String("public")); - attributes.insert(QLatin1String("static"), noAttributeValue()); - break; - case StackElement::ModifyFunction: - attributes.insert(QLatin1String("signature"), QString()); - attributes.insert(QLatin1String("access"), QString()); - attributes.insert(QLatin1String("remove"), QString()); - attributes.insert(QLatin1String("rename"), QString()); - attributes.insert(QLatin1String("deprecated"), noAttributeValue()); - attributes.insert(QLatin1String("associated-to"), QString()); - attributes.insert(QLatin1String("virtual-slot"), noAttributeValue()); - attributes.insert(QLatin1String("thread"), noAttributeValue()); - attributes.insert(QLatin1String("allow-thread"), noAttributeValue()); - break; - case StackElement::ModifyArgument: - attributes.insert(QLatin1String("index"), QString()); - attributes.insert(QLatin1String("replace-value"), QString()); - attributes.insert(QLatin1String("invalidate-after-use"), noAttributeValue()); - break; - case StackElement::ModifyField: - attributes.insert(nameAttribute(), QString()); - attributes.insert(QLatin1String("write"), trueAttributeValue()); - attributes.insert(QLatin1String("read"), trueAttributeValue()); - attributes.insert(QLatin1String("remove"), QString()); - break; - case StackElement::Access: - attributes.insert(QLatin1String("modifier"), QString()); - break; - case StackElement::Include: - attributes.insert(QLatin1String("file-name"), QString()); - attributes.insert(QLatin1String("location"), QString()); - break; - case StackElement::CustomMetaConstructor: - attributes[nameAttribute()] = topElement.entry->name().toLower() + QLatin1String("_create"); - attributes.insert(QLatin1String("param-name"), QLatin1String("copy")); + if (!loadTypesystem(reader, &attributes)) + return false; break; - case StackElement::CustomMetaDestructor: - attributes[nameAttribute()] = topElement.entry->name().toLower() + QLatin1String("_delete"); - attributes.insert(QLatin1String("param-name"), QLatin1String("copy")); + case StackElement::RejectEnumValue: + if (!parseRejectEnumValue(reader, &attributes)) + return false; break; case StackElement::ReplaceType: - attributes.insert(QLatin1String("modified-type"), QString()); - break; - case StackElement::InjectCode: - attributes.insert(QLatin1String("class"), QLatin1String("target")); - attributes.insert(QLatin1String("position"), QLatin1String("beginning")); - attributes.insert(QLatin1String("file"), QString()); + if (!parseReplaceArgumentType(reader, topElement, &attributes)) + return false; break; case StackElement::ConversionRule: - attributes.insert(QLatin1String("class"), QString()); - attributes.insert(QLatin1String("file"), QString()); - break; - case StackElement::TargetToNative: - attributes.insert(QLatin1String("replace"), yesAttributeValue()); - break; - case StackElement::AddConversion: - attributes.insert(QLatin1String("type"), QString()); - attributes.insert(QLatin1String("check"), QString()); - break; - case StackElement::RejectEnumValue: - attributes.insert(nameAttribute(), QString()); - break; - case StackElement::ArgumentMap: - attributes.insert(QLatin1String("index"), QLatin1String("1")); - attributes.insert(QLatin1String("meta-name"), QString()); - break; - case StackElement::Rename: - attributes.insert(QLatin1String("to"), QString()); - break; - case StackElement::Rejection: - 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")); - break; - case StackElement::Template: - attributes.insert(nameAttribute(), QString()); - break; - case StackElement::TemplateInstanceEnum: - attributes.insert(nameAttribute(), QString()); - break; - case StackElement::Replace: - attributes.insert(QLatin1String("from"), QString()); - attributes.insert(QLatin1String("to"), QString()); - break; - case StackElement::ReferenceCount: - attributes.insert(QLatin1String("action"), QString()); - attributes.insert(QLatin1String("variable-name"), QString()); - break; - case StackElement::ParentOwner: - attributes.insert(QLatin1String("index"), QString()); - attributes.insert(QLatin1String("action"), QString()); - break; - case StackElement::Array: - break; - default: - { }; - }; - - if (!attributes.isEmpty()) - fetchAttributeValues(tagName, atts, &attributes); - - switch (element->type) { - case StackElement::Root: - m_defaultPackage = attributes[QLatin1String("package")]; - m_defaultSuperclass = attributes[QLatin1String("default-superclass")]; - element->type = StackElement::Root; - { - TypeSystemTypeEntry* moduleEntry = reinterpret_cast<TypeSystemTypeEntry*>( - m_database->findType(m_defaultPackage)); - element->entry = moduleEntry ? moduleEntry : new TypeSystemTypeEntry(m_defaultPackage, since); - element->entry->setCodeGeneration(m_generate); - } - - if ((m_generate == TypeEntry::GenerateForSubclass || - m_generate == TypeEntry::GenerateNothing) && !m_defaultPackage.isEmpty()) - TypeDatabase::instance()->addRequiredTargetImport(m_defaultPackage); - - if (!element->entry->qualifiedCppName().isEmpty()) - m_database->addType(element->entry); - break; - case StackElement::LoadTypesystem: { - QString name = attributes[nameAttribute()]; - if (name.isEmpty()) { - m_error = QLatin1String("No typesystem name specified"); + if (!Handler::parseCustomConversion(reader, topElement, &attributes)) return false; - } - bool generateChild = (convertBoolean(attributes[QLatin1String("generate")], QLatin1String("generate"), true) && (m_generate == TypeEntry::GenerateAll)); - if (!m_database->parseFile(name, m_currentPath, generateChild)) { - m_error = QStringLiteral("Failed to parse: '%1'").arg(name); - return false; - } - } - break; - case StackElement::RejectEnumValue: - if (!m_currentEnum) { - m_error = QLatin1String("<reject-enum-value> node must be used inside a <enum-type> node"); - return false; - } - break; - case StackElement::ReplaceType: { - if (topElement.type != StackElement::ModifyArgument) { - m_error = QLatin1String("Type replacement can only be specified for argument modifications"); - return false; - } - - if (attributes[QLatin1String("modified-type")].isEmpty()) { - m_error = QLatin1String("Type replacement requires 'modified-type' attribute"); - return false; - } - - m_contextStack.top()->functionMods.last().argument_mods.last().modified_type = attributes[QLatin1String("modified-type")]; - } - break; - case StackElement::ConversionRule: { - 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; - } - - static QHash<QString, TypeSystem::Language> languageNames; - if (languageNames.isEmpty()) { - languageNames[QLatin1String("target")] = TypeSystem::TargetLangCode; - languageNames[QLatin1String("native")] = TypeSystem::NativeCode; - } - - QString languageAttribute = attributes[QLatin1String("class")].toLower(); - TypeSystem::Language lang = languageNames.value(languageAttribute, TypeSystem::NoLanguage); - - if (topElement.type == StackElement::ModifyArgument) { - if (lang == TypeSystem::NoLanguage) { - m_error = QStringLiteral("unsupported class attribute: '%1'").arg(lang); - return false; - } - - CodeSnip snip; - snip.language = lang; - m_contextStack.top()->functionMods.last().argument_mods.last().conversion_rules.append(snip); - } else { - 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. - QString sourceFile = attributes[QLatin1String("file")]; - 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)) { - topElement.entry->setConversionRule(QLatin1String(conversionFlag) + QString::fromUtf8(conversionSource.readAll())); - } else { - qCWarning(lcShiboken).noquote().nospace() - << "File containing conversion code for " - << topElement.entry->name() << " type does not exist or is not readable: " - << sourceFile; - } - } - } - - CustomConversion* customConversion = new CustomConversion(static_cast<TypeEntry*>(m_current->entry)); - customConversionsForReview.append(customConversion); - } - } - break; - case StackElement::NativeToTarget: { - if (topElement.type != StackElement::ConversionRule) { - m_error = QLatin1String("Native to Target conversion code can only be specified for custom conversion rules."); + break; + case StackElement::NativeToTarget: + if (!parseNativeToTarget(reader, topElement, &attributes)) return false; - } - m_contextStack.top()->codeSnips << CodeSnip(); - } - break; + 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; } - bool replace = attributes[QLatin1String("replace")] == yesAttributeValue(); - static_cast<TypeEntry*>(m_current->entry)->customConversion()->setReplaceOriginalTargetToNativeConversions(replace); - } - break; - case StackElement::AddConversion: { - 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 = attributes[QLatin1String("type")]; - if (sourceTypeName.isEmpty()) { - m_error = QLatin1String("Target to Native conversions must specify the input type with the 'type' attribute."); - return false; - } - QString typeCheck = attributes[QLatin1String("check")]; - static_cast<TypeEntry*>(m_current->entry)->customConversion()->addTargetToNativeConversion(sourceTypeName, typeCheck); - m_contextStack.top()->codeSnips << CodeSnip(); - } - break; - case StackElement::ModifyArgument: { - 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 = attributes[QLatin1String("index")]; - if (index == QLatin1String("return")) - index = QLatin1String("0"); - else if (index == QLatin1String("this")) - index = QLatin1String("-1"); - - bool ok = false; - int idx = index.toInt(&ok); - if (!ok) { - m_error = QStringLiteral("Cannot convert '%1' to integer").arg(index); - return false; - } - - QString replace_value = attributes[QLatin1String("replace-value")]; - - if (!replace_value.isEmpty() && idx) { - m_error = QLatin1String("replace-value is only supported for return values (index=0)."); - return false; - } - - 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); + 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::NoNullPointers: { - if (topElement.type != StackElement::ModifyArgument) { - m_error = QLatin1String("no-null-pointer requires argument modification as parent"); + case StackElement::AddConversion: + if (!parseAddConversion(reader, topElement, &attributes)) return false; - } - - m_contextStack.top()->functionMods.last().argument_mods.last().noNullPointers = true; - if (!m_contextStack.top()->functionMods.last().argument_mods.last().index) - m_contextStack.top()->functionMods.last().argument_mods.last().nullPointerDefaultValue = attributes[QLatin1String("default-value")]; - else if (!attributes[QLatin1String("default-value")].isEmpty()) - qCWarning(lcShiboken) << "default values for null pointer guards are only effective for return values"; - - } - break; - case StackElement::DefineOwnership: { - if (topElement.type != StackElement::ModifyArgument) { - m_error = QLatin1String("define-ownership requires argument modification as parent"); + break; + case StackElement::ModifyArgument: + if (!parseModifyArgument(reader, topElement, &attributes)) return false; - } - - static QHash<QString, TypeSystem::Language> languageNames; - if (languageNames.isEmpty()) { - languageNames[QLatin1String("target")] = TypeSystem::TargetLangCode; - languageNames[QLatin1String("native")] = TypeSystem::NativeCode; - } - - QString classAttribute = attributes[QLatin1String("class")].toLower(); - TypeSystem::Language lang = languageNames.value(classAttribute, TypeSystem::NoLanguage); - if (lang == TypeSystem::NoLanguage) { - m_error = QStringLiteral("unsupported class attribute: '%1'").arg(classAttribute); + break; + case StackElement::NoNullPointers: + if (!parseNoNullPointer(reader, topElement, &attributes)) return false; - } - - static QHash<QString, TypeSystem::Ownership> ownershipNames; - if (ownershipNames.isEmpty()) { - ownershipNames[QLatin1String("target")] = TypeSystem::TargetLangOwnership; - ownershipNames[QLatin1String("c++")] = TypeSystem::CppOwnership; - ownershipNames[QLatin1String("default")] = TypeSystem::DefaultOwnership; - } - - QString ownershipAttribute = attributes[QLatin1String("owner")].toLower(); - TypeSystem::Ownership owner = ownershipNames.value(ownershipAttribute, TypeSystem::InvalidOwnership); - if (owner == TypeSystem::InvalidOwnership) { - m_error = QStringLiteral("unsupported owner attribute: '%1'").arg(ownershipAttribute); + break; + case StackElement::DefineOwnership: + if (!parseDefineOwnership(reader, topElement, &attributes)) return false; - } - - m_contextStack.top()->functionMods.last().argument_mods.last().ownerships[lang] = owner; - } - break; + break; case StackElement::SuppressedWarning: { - const QString suppressedWarning = attributes.value(textAttribute()); - if (suppressedWarning.isEmpty()) { + 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: { - if (!(topElement.type & StackElement::CodeSnipMask)) { - m_error = QLatin1String("Argument maps requires code injection as parent"); - return false; - } - - bool ok; - int pos = attributes[QLatin1String("index")].toInt(&ok); - if (!ok) { - m_error = QStringLiteral("Can't convert position '%1' to integer") - .arg(attributes[QLatin1String("position")]); - return false; - } - - if (pos <= 0) { - m_error = QStringLiteral("Argument position %1 must be a positive number").arg(pos); - return false; - } - - QString meta_name = attributes[QLatin1String("meta-name")]; - if (meta_name.isEmpty()) - qCWarning(lcShiboken) << "Empty meta name in argument map"; - - - if (topElement.type == StackElement::InjectCodeInFunction) - m_contextStack.top()->functionMods.last().snips.last().argumentMap[pos] = meta_name; - else { - qCWarning(lcShiboken) << "Argument maps are only useful for injection of code " - "into functions."; - } - } - break; - case StackElement::Removal: { - if (topElement.type != StackElement::ModifyFunction) { - m_error = QLatin1String("Function modification parent required"); + case StackElement::ArgumentMap: + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedElementWarning(reader, tagName))); + if (!parseArgumentMap(reader, topElement, &attributes)) return false; - } - - static QHash<QString, TypeSystem::Language> languageNames; - if (languageNames.isEmpty()) { - languageNames.insert(QLatin1String("target"), TypeSystem::TargetLangAndNativeCode); - languageNames.insert(QLatin1String("all"), TypeSystem::All); - } - - QString languageAttribute = attributes[QLatin1String("class")].toLower(); - TypeSystem::Language lang = languageNames.value(languageAttribute, TypeSystem::NoLanguage); - if (lang == TypeSystem::NoLanguage) { - m_error = QStringLiteral("unsupported class attribute: '%1'").arg(languageAttribute); + break; + case StackElement::Removal: + if (!parseRemoval(reader, topElement, &attributes)) return false; - } - - m_contextStack.top()->functionMods.last().removal = lang; - } - break; + break; case StackElement::Rename: - case StackElement::Access: { - 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 = 0; - if (topElement.type == StackElement::ModifyFunction) - mod = &m_contextStack.top()->functionMods.last(); - else if (topElement.type == StackElement::ModifyField) - mod = &m_contextStack.top()->fieldMods.last(); - - QString modifier; - if (element->type == StackElement::Rename) { - modifier = QLatin1String("rename"); - QString renamed_to = attributes[QLatin1String("to")]; - if (renamed_to.isEmpty()) { - m_error = QLatin1String("Rename modifier requires 'to' attribute"); - return false; - } - - 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 - modifier = attributes[QLatin1String("modifier")].toLower(); - - - if (modifier.isEmpty()) { - m_error = QLatin1String("No access modification specified"); - return false; - } - - static QHash<QString, FunctionModification::Modifiers> modifierNames; - if (modifierNames.isEmpty()) { - modifierNames[QLatin1String("private")] = Modification::Private; - modifierNames[QLatin1String("public")] = Modification::Public; - modifierNames[QLatin1String("protected")] = Modification::Protected; - modifierNames[QLatin1String("friendly")] = Modification::Friendly; - modifierNames[QLatin1String("rename")] = Modification::Rename; - modifierNames[QLatin1String("final")] = Modification::Final; - modifierNames[QLatin1String("non-final")] = Modification::NonFinal; - } - - if (!modifierNames.contains(modifier)) { - m_error = QStringLiteral("Unknown access modifier: '%1'").arg(modifier); + case StackElement::Access: + if (!parseRename(reader, element->type, topElement, &attributes)) return false; - } - - if (mod) - mod->modifiers |= modifierNames[modifier]; - } - break; + break; case StackElement::RemoveArgument: if (topElement.type != StackElement::ModifyArgument) { m_error = QLatin1String("Removing argument requires argument modification as parent"); @@ -1691,229 +2755,38 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts m_contextStack.top()->functionMods.last().argument_mods.last().removed = true; break; - case StackElement::ModifyField: { - QString name = attributes[nameAttribute()]; - if (name.isEmpty()) - break; - FieldModification fm; - fm.name = name; - fm.modifiers = 0; - - if (!convertRemovalAttribute(attributes[QLatin1String("remove")], fm, m_error)) - return false; - - QString read = attributes[QLatin1String("read")]; - QString write = attributes[QLatin1String("write")]; - - if (read == trueAttributeValue()) fm.modifiers |= FieldModification::Readable; - if (write == trueAttributeValue()) fm.modifiers |= FieldModification::Writable; - - m_contextStack.top()->fieldMods << fm; - } - break; - case StackElement::AddFunction: { - 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; - } - const QString originalSignature = attributes[QLatin1String("signature")]; - - 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; - } - - AddedFunction func(signature, attributes[QLatin1String("return-type")]); - func.setStatic(attributes[QLatin1String("static")] == yesAttributeValue()); - if (!signature.contains(QLatin1Char('('))) - signature += QLatin1String("()"); - m_currentSignature = signature; - - QString access = attributes[QLatin1String("access")].toLower(); - if (!access.isEmpty()) { - if (access == QLatin1String("protected")) { - func.setAccess(AddedFunction::Protected); - } else if (access == QLatin1String("public")) { - func.setAccess(AddedFunction::Public); - } else { - m_error = QString::fromLatin1("Bad access type '%1'").arg(access); - return false; - } - } - - m_contextStack.top()->addedFunctions << func; - - FunctionModification mod; - if (!mod.setSignature(m_currentSignature, &m_error)) - return false; - mod.setOriginalSignature(originalSignature); - m_contextStack.top()->functionMods << mod; - } - break; - case StackElement::ModifyFunction: { - 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; - } - const QString originalSignature = attributes[QLatin1String("signature")]; - - 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; + case StackElement::ModifyField: + if (!parseModifyField(reader, &attributes)) return false; - } - - FunctionModification mod; - if (!mod.setSignature(signature, &m_error)) + break; + case StackElement::AddFunction: + if (!parseAddFunction(reader, topElement, &attributes)) return false; - mod.setOriginalSignature(originalSignature); - m_currentSignature = signature; - - QString access = attributes[QLatin1String("access")].toLower(); - if (!access.isEmpty()) { - if (access == QLatin1String("private")) - mod.modifiers |= Modification::Private; - else if (access == QLatin1String("protected")) - mod.modifiers |= Modification::Protected; - else if (access == QLatin1String("public")) - mod.modifiers |= Modification::Public; - else if (access == QLatin1String("final")) - mod.modifiers |= Modification::Final; - else if (access == QLatin1String("non-final")) - mod.modifiers |= Modification::NonFinal; - else { - m_error = QString::fromLatin1("Bad access type '%1'").arg(access); - return false; - } - } - - if (convertBoolean(attributes[QLatin1String("deprecated")], QLatin1String("deprecated"), false)) - mod.modifiers |= Modification::Deprecated; - - if (!convertRemovalAttribute(attributes[QLatin1String("remove")], mod, m_error)) + break; + case StackElement::ModifyFunction: + if (!parseModifyFunction(reader, topElement, &attributes)) return false; - - QString rename = attributes[QLatin1String("rename")]; - if (!rename.isEmpty()) { - mod.renamedToName = rename; - mod.modifiers |= Modification::Rename; - } - - QString association = attributes[QLatin1String("associated-to")]; - if (!association.isEmpty()) - mod.association = association; - - mod.setIsThread(convertBoolean(attributes[QLatin1String("thread")], QLatin1String("thread"), false)); - mod.setAllowThread(convertBoolean(attributes[QLatin1String("allow-thread")], QLatin1String("allow-thread"), false)); - - mod.modifiers |= (convertBoolean(attributes[QLatin1String("virtual-slot")], QLatin1String("virtual-slot"), false) ? Modification::VirtualSlot : 0); - - m_contextStack.top()->functionMods << mod; - } - break; + break; case StackElement::ReplaceDefaultExpression: - if (!(topElement.type & StackElement::ModifyArgument)) { - m_error = QLatin1String("Replace default expression only allowed as child of argument modification"); - return false; - } - - if (attributes[QLatin1String("with")].isEmpty()) { - m_error = QLatin1String("Default expression replaced with empty string. Use remove-default-expression instead."); + if (!parseReplaceDefaultExpression(reader, topElement, &attributes)) return false; - } - - m_contextStack.top()->functionMods.last().argument_mods.last().replacedDefaultExpression = attributes[QLatin1String("with")]; break; case StackElement::RemoveDefaultExpression: m_contextStack.top()->functionMods.last().argument_mods.last().removedDefaultExpression = true; break; case StackElement::CustomMetaConstructor: - case StackElement::CustomMetaDestructor: { - CustomFunction *func = new CustomFunction(attributes[nameAttribute()]); - func->paramName = attributes[QLatin1String("param-name")]; - element->value.customFunction = func; - } - break; - case StackElement::ReferenceCount: { - if (topElement.type != StackElement::ModifyArgument) { - m_error = QLatin1String("reference-count must be child of modify-argument"); - return false; - } - - ReferenceCount rc; - - static QHash<QString, ReferenceCount::Action> actions; - if (actions.isEmpty()) { - actions[QLatin1String("add")] = ReferenceCount::Add; - actions[QLatin1String("add-all")] = ReferenceCount::AddAll; - actions[QLatin1String("remove")] = ReferenceCount::Remove; - actions[QLatin1String("set")] = ReferenceCount::Set; - actions[QLatin1String("ignore")] = ReferenceCount::Ignore; - } - rc.action = actions.value(attributes[QLatin1String("action")].toLower(), ReferenceCount::Invalid); - rc.varName = attributes[QLatin1String("variable-name")]; - - if (rc.action == ReferenceCount::Invalid) { - m_error = QLatin1String("unrecognized value for action attribute. supported actions:"); - 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); - } - break; - - case StackElement::ParentOwner: { - if (topElement.type != StackElement::ModifyArgument) { - m_error = QLatin1String("parent-policy must be child of modify-argument"); - return false; - } - - ArgumentOwner ao; - - QString index = attributes[QLatin1String("index")]; - if (index == QLatin1String("return")) - index = QLatin1String("0"); - else if (index == QLatin1String("this")) - index = QLatin1String("-1"); - - bool ok = false; - int idx = index.toInt(&ok); - if (!ok) { - m_error = QStringLiteral("Cannot convert '%1' to integer").arg(index); + case StackElement::CustomMetaDestructor: + element->value.customFunction = + parseCustomMetaConstructor(reader, element->type, topElement, &attributes); + break; + case StackElement::ReferenceCount: + if (!parseReferenceCount(reader, topElement, &attributes)) return false; - } - - static QHash<QString, ArgumentOwner::Action> actions; - if (actions.isEmpty()) { - actions[QLatin1String("add")] = ArgumentOwner::Add; - actions[QLatin1String("remove")] = ArgumentOwner::Remove; - } - - ao.action = actions.value(attributes[QLatin1String("action")].toLower(), ArgumentOwner::Invalid); - if (!ao.action) { - m_error = QLatin1String("Invalid parent actionr"); + break; + case StackElement::ParentOwner: + if (!parseParentOwner(reader, topElement, &attributes)) return false; - } - ao.index = idx; - m_contextStack.top()->functionMods.last().argument_mods.last().owner = ao; - } - break; + break; case StackElement::Array: if (topElement.type != StackElement::ModifyArgument) { m_error = QLatin1String("array must be child of modify-argument"); @@ -1921,185 +2794,54 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts } m_contextStack.top()->functionMods.last().argument_mods.last().array = true; break; - case StackElement::InjectCode: { - 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; - } - - static QHash<QString, TypeSystem::Language> languageNames; - if (languageNames.isEmpty()) { - languageNames[QLatin1String("target")] = TypeSystem::TargetLangCode; // em algum lugar do cpp - languageNames[QLatin1String("native")] = TypeSystem::NativeCode; // em algum lugar do cpp - languageNames[QLatin1String("shell")] = TypeSystem::ShellCode; // coloca no header, mas antes da declaracao da classe - languageNames[QLatin1String("shell-declaration")] = TypeSystem::ShellDeclaration; // coloca no header, dentro da declaracao da classe - languageNames[QLatin1String("library-initializer")] = TypeSystem::PackageInitializer; - languageNames[QLatin1String("destructor-function")] = TypeSystem::DestructorFunction; - languageNames[QLatin1String("constructors")] = TypeSystem::Constructors; - languageNames[QLatin1String("interface")] = TypeSystem::Interface; - } - - QString className = attributes[QLatin1String("class")].toLower(); - if (!languageNames.contains(className)) { - m_error = QStringLiteral("Invalid class specifier: '%1'").arg(className); - return false; - } - - - static QHash<QString, TypeSystem::CodeSnipPosition> positionNames; - if (positionNames.isEmpty()) { - positionNames.insert(QLatin1String("beginning"), TypeSystem::CodeSnipPositionBeginning); - positionNames.insert(QLatin1String("end"), TypeSystem::CodeSnipPositionEnd); - // QtScript - positionNames.insert(QLatin1String("declaration"), TypeSystem::CodeSnipPositionDeclaration); - positionNames.insert(QLatin1String("prototype-initialization"), TypeSystem::CodeSnipPositionPrototypeInitialization); - positionNames.insert(QLatin1String("constructor-initialization"), TypeSystem::CodeSnipPositionConstructorInitialization); - positionNames.insert(QLatin1String("constructor"), TypeSystem::CodeSnipPositionConstructor); - } - - QString position = attributes[QLatin1String("position")].toLower(); - if (!positionNames.contains(position)) { - m_error = QStringLiteral("Invalid position: '%1'").arg(position); + case StackElement::InjectCode: + if (!parseInjectCode(reader, topElement, element, &attributes)) return false; - } - - CodeSnip snip; - snip.language = languageNames[className]; - snip.position = positionNames[position]; - bool in_file = false; - - QString file_name = attributes[QLatin1String("file")]; - - //Handler constructor.... - if (m_generate != TypeEntry::GenerateForSubclass && - m_generate != TypeEntry::GenerateNothing && - !file_name.isEmpty()) { - 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: "); - content += file_name; - content += QLatin1String("]\n"); - content += QString::fromUtf8(codeFile.readAll()); - content += QLatin1String("\n// END of custom code block [file: "); - content += file_name; - content += QLatin1String("]\n// ========================================================================\n"); - snip.addCode(content); - in_file = true; - } - } else { - qCWarning(lcShiboken).noquote().nospace() - << "File for inject code not exist: " << QDir::toNativeSeparators(file_name); - } - - } - - if (snip.language == TypeSystem::Interface && topElement.type != StackElement::InterfaceTypeEntry) { - m_error = QLatin1String("Interface code injections must be direct child of an interface type entry"); + break; + case StackElement::Include: + if (!parseInclude(reader, topElement, element->entry, &attributes)) return false; - } - - if (topElement.type == StackElement::ModifyFunction || topElement.type == StackElement::AddFunction) { - 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; - } - - m_contextStack.top()->functionMods.last().snips << snip; - if (in_file) - m_contextStack.top()->functionMods.last().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; - } - - } - break; - case StackElement::Include: { - QString location = attributes[QLatin1String("location")].toLower(); - - static QHash<QString, Include::IncludeType> locationNames; - if (locationNames.isEmpty()) { - locationNames[QLatin1String("global")] = Include::IncludePath; - locationNames[QLatin1String("local")] = Include::LocalPath; - locationNames[QLatin1String("target")] = Include::TargetLangImport; - } - - if (!locationNames.contains(location)) { - m_error = QStringLiteral("Location not recognized: '%1'").arg(location); + break; + case StackElement::Rejection: + if (!addRejection(m_database, &attributes, &m_error)) return false; - } - - Include::IncludeType loc = locationNames[location]; - Include inc(loc, attributes[QLatin1String("file-name")]); - - ComplexTypeEntry *ctype = static_cast<ComplexTypeEntry *>(element->entry); - if (topElement.type & (StackElement::ComplexTypeEntryMask | StackElement::PrimitiveTypeEntry)) { - element->entry->setInclude(inc); - } else if (topElement.type == StackElement::ExtraIncludes) { - element->entry->addExtraInclude(inc); - } else { - m_error = QLatin1String("Only supported parent tags are primitive-type, complex types or extra-includes"); + break; + case StackElement::Template: { + const int nameIndex = indexOfAttribute(attributes, nameAttribute()); + if (nameIndex == -1) { + m_error = msgMissingAttribute(nameAttribute()); return false; } - - inc = ctype->include(); - IncludeList lst = ctype->extraIncludes(); - ctype = ctype->designatedInterface(); - if (ctype) { - ctype->setExtraIncludes(lst); - ctype->setInclude(inc); - } + element->value.templateEntry = + new TemplateEntry(attributes.takeAt(nameIndex).value().toString()); } - break; - case StackElement::Rejection: - if (!addRejection(m_database, attributes, &m_error)) - return false; - break; - case StackElement::Template: - element->value.templateEntry = new TemplateEntry(attributes.value(nameAttribute())); break; case StackElement::TemplateInstanceEnum: - 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."); + element->value.templateInstance = + parseTemplateInstanceEnum(reader, topElement, &attributes); + if (!element->value.templateInstance) return false; - } - element->value.templateInstance = new TemplateInstance(attributes.value(nameAttribute())); break; case StackElement::Replace: - if (topElement.type != StackElement::TemplateInstanceEnum) { - m_error = QLatin1String("Can only insert replace rules into insert-template."); + if (!parseReplace(reader, topElement, element, &attributes)) return false; - } - element->parent->value.templateInstance->addReplaceRule(attributes[QLatin1String("from")], attributes[QLatin1String("to")]); 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_preferredConversion(true), m_preferredTargetLangType(true) { } @@ -2120,35 +2862,15 @@ PrimitiveTypeEntry *PrimitiveTypeEntry::basicReferencedTypeEntry() const return 0; PrimitiveTypeEntry *baseReferencedTypeEntry = m_referencedTypeEntry->basicReferencedTypeEntry(); - if (baseReferencedTypeEntry) - return baseReferencedTypeEntry; - else - return m_referencedTypeEntry; -} - -bool PrimitiveTypeEntry::preferredConversion() const -{ - return m_preferredConversion; + return baseReferencedTypeEntry ? baseReferencedTypeEntry : m_referencedTypeEntry; } -void PrimitiveTypeEntry::setPreferredConversion(bool b) +TypeEntry *PrimitiveTypeEntry::clone() const { - m_preferredConversion = b; + return new PrimitiveTypeEntry(*this); } -typedef QHash<const PrimitiveTypeEntry*, QString> PrimitiveTypeEntryTargetLangPackageMap; -Q_GLOBAL_STATIC(PrimitiveTypeEntryTargetLangPackageMap, primitiveTypeEntryTargetLangPackages); - -void PrimitiveTypeEntry::setTargetLangPackage(const QString& package) -{ - primitiveTypeEntryTargetLangPackages()->insert(this, package); -} -QString PrimitiveTypeEntry::targetLangPackage() const -{ - if (!primitiveTypeEntryTargetLangPackages()->contains(this)) - return this->::TypeEntry::targetLangPackage(); - return primitiveTypeEntryTargetLangPackages()->value(this); -} +PrimitiveTypeEntry::PrimitiveTypeEntry(const PrimitiveTypeEntry &) = default; CodeSnipList TypeEntry::codeSnips() const { @@ -2186,42 +2908,42 @@ 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); - void ComplexTypeEntry::setDefaultConstructor(const QString& defaultConstructor) { - if (!defaultConstructor.isEmpty()) - complexTypeEntryDefaultConstructors()->insert(this, defaultConstructor); + m_defaultConstructor = defaultConstructor; } QString ComplexTypeEntry::defaultConstructor() const { - if (!complexTypeEntryDefaultConstructors()->contains(this)) - return QString(); - return complexTypeEntryDefaultConstructors()->value(this); + return m_defaultConstructor; } bool ComplexTypeEntry::hasDefaultConstructor() const { - return complexTypeEntryDefaultConstructors()->contains(this); + return !m_defaultConstructor.isEmpty(); } -QString ContainerTypeEntry::targetLangPackage() const +TypeEntry *ComplexTypeEntry::clone() const { - return QString(); + return new ComplexTypeEntry(*this); } +// Take over parameters relevant for typedefs +void ComplexTypeEntry::useAsTypedef(const ComplexTypeEntry *source) +{ + TypeEntry::useAsTypedef(source); + m_qualifiedCppName = source->m_qualifiedCppName; + m_targetLangName = source->m_targetLangName; + m_lookupName = source->m_lookupName; + m_targetType = source->m_targetType; +} + +ComplexTypeEntry::ComplexTypeEntry(const ComplexTypeEntry &) = default; + QString ContainerTypeEntry::targetLangName() const { @@ -2252,13 +2974,17 @@ QString ContainerTypeEntry::qualifiedCppName() const return ComplexTypeEntry::qualifiedCppName(); } +TypeEntry *ContainerTypeEntry::clone() const +{ + return new ContainerTypeEntry(*this); +} + +ContainerTypeEntry::ContainerTypeEntry(const ContainerTypeEntry &) = default; + QString EnumTypeEntry::targetLangQualifier() const { TypeEntry *te = TypeDatabase::instance()->findType(m_qualifier); - if (te) - return te->targetLangName(); - else - return m_qualifier; + return te ? te->targetLangName() : m_qualifier; } QString EnumTypeEntry::qualifiedTargetLangName() const @@ -2281,26 +3007,25 @@ QString EnumTypeEntry::targetLangApiName() const return QLatin1String("jint"); } -bool EnumTypeEntry::preferredConversion() const -{ - return false; -} - QString FlagsTypeEntry::targetLangApiName() const { return QLatin1String("jint"); } -bool FlagsTypeEntry::preferredConversion() const +TypeEntry *EnumTypeEntry::clone() const { - return false; + return new EnumTypeEntry(*this); } -QString FlagsTypeEntry::targetLangPackage() const +EnumTypeEntry::EnumTypeEntry(const EnumTypeEntry &) = default; + +TypeEntry *FlagsTypeEntry::clone() const { - return m_enum->targetLangPackage(); + return new FlagsTypeEntry(*this); } +FlagsTypeEntry::FlagsTypeEntry(const FlagsTypeEntry &) = default; + QString FlagsTypeEntry::qualifiedTargetLangName() const { return targetLangPackage() + QLatin1Char('.') + m_enum->targetLangQualifier() @@ -2320,7 +3045,7 @@ QString fixCppTypeName(const QString &name) { if (name == QLatin1String("long long")) return QLatin1String("qint64"); - else if (name == QLatin1String("unsigned long long")) + if (name == QLatin1String("unsigned long long")) return QLatin1String("quint64"); return name; } @@ -2341,11 +3066,9 @@ QString TemplateInstance::expandCode() const result += code; result += QLatin1String("\n// TEMPLATE - ") + m_name + QLatin1String(" - END"); return result; - } else { - qCWarning(lcShiboken).noquote().nospace() - << "insert-template referring to non-existing template '" << m_name << '\''; } - + qCWarning(lcShiboken).noquote().nospace() + << "insert-template referring to non-existing template '" << m_name << '\''; return QString(); } @@ -2361,10 +3084,7 @@ QString CodeSnipAbstract::code() const QString CodeSnipFragment::code() const { - if (m_instance) - return m_instance->expandCode(); - else - return m_code; + return m_instance ? m_instance->expandCode() : m_code; } bool FunctionModification::setSignature(const QString &s, QString *errorMessage) @@ -2513,6 +3233,122 @@ AddedFunction::AddedFunction(QString signature, const QString &returnType) : } #ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug d, const ReferenceCount &r) +{ + QDebugStateSaver saver(d); + d.noquote(); + d.nospace(); + d << "ReferenceCount(" << r.varName << ", action=" << r.action << ')'; + return d; +} + +QDebug operator<<(QDebug d, const CodeSnip &s) +{ + QDebugStateSaver saver(d); + d.noquote(); + d.nospace(); + d << "CodeSnip(language=" << s.language << ", position=" << s.position << ", \""; + for (const auto &f : s.codeList) { + const QString &code = f.code(); + const auto lines = code.splitRef(QLatin1Char('\n')); + for (int i = 0, size = lines.size(); i < size; ++i) { + if (i) + d << "\\n"; + d << lines.at(i).trimmed(); + } + } + d << '"'; + if (!s.argumentMap.isEmpty()) { + d << ", argumentMap{"; + for (auto it = s.argumentMap.cbegin(), end = s.argumentMap.cend(); it != end; ++it) + d << it.key() << "->\"" << it.value() << '"'; + d << '}'; + } + d << ')'; + return d; +} + +void Modification::formatDebug(QDebug &d) const +{ + d << "modifiers=" << hex << showbase << modifiers << noshowbase << dec; + if (removal) + d << ", removal"; + if (!renamedToName.isEmpty()) + d << ", renamedToName=\"" << renamedToName << '"'; +} + +void FunctionModification::formatDebug(QDebug &d) const +{ + if (m_signature.isEmpty()) + d << "pattern=\"" << m_signaturePattern.pattern(); + else + d << "signature=\"" << m_signature; + d << "\", "; + Modification::formatDebug(d); + if (!association.isEmpty()) + d << ", association=\"" << association << '"'; + if (m_allowThread != TypeSystem::AllowThread::Unspecified) + d << ", allowThread=" << int(m_allowThread); + if (m_thread) + d << ", thread"; + if (m_exceptionHandling != TypeSystem::ExceptionHandling::Unspecified) + d << ", exceptionHandling=" << int(m_exceptionHandling); + if (!snips.isEmpty()) + d << ", snips=(" << snips << ')'; + if (!argument_mods.isEmpty()) + d << ", argument_mods=(" << argument_mods << ')'; +} + +QDebug operator<<(QDebug d, const ArgumentOwner &a) +{ + QDebugStateSaver saver(d); + d.noquote(); + d.nospace(); + d << "ArgumentOwner(index=" << a.index << ", action=" << a.action << ')'; + return d; +} + +QDebug operator<<(QDebug d, const ArgumentModification &a) +{ + QDebugStateSaver saver(d); + d.noquote(); + d.nospace(); + d << "ArgumentModification(index=" << a.index; + if (a.removedDefaultExpression) + d << ", removedDefaultExpression"; + if (a.removed) + d << ", removed"; + if (a.noNullPointers) + d << ", noNullPointers"; + if (a.array) + d << ", array"; + if (!a.referenceCounts.isEmpty()) + d << ", referenceCounts=" << a.referenceCounts; + if (!a.modified_type.isEmpty()) + d << ", modified_type=\"" << a.modified_type << '"'; + if (!a.replace_value.isEmpty()) + d << ", replace_value=\"" << a.replace_value << '"'; + if (!a.replacedDefaultExpression.isEmpty()) + d << ", replacedDefaultExpression=\"" << a.replacedDefaultExpression << '"'; + if (!a.ownerships.isEmpty()) + d << ", ownerships=" << a.ownerships; + if (!a.renamed_to.isEmpty()) + d << ", renamed_to=\"" << a.renamed_to << '"'; + d << ", owner=" << a.owner << ')'; + return d; +} + +QDebug operator<<(QDebug d, const FunctionModification &fm) +{ + QDebugStateSaver saver(d); + d.noquote(); + d.nospace(); + d << "FunctionModification("; + fm.formatDebug(d); + d << ')'; + return d; +} + QDebug operator<<(QDebug d, const AddedFunction::TypeInfo &ti) { QDebugStateSaver saver(d); @@ -2556,11 +3392,12 @@ AddedFunction::TypeInfo AddedFunction::TypeInfo::fromSignature(const QString& si ComplexTypeEntry::ComplexTypeEntry(const QString &name, TypeEntry::Type t, const QVersionNumber &vr) : - TypeEntry(QString(name).replace(QLatin1String(".*::"), QString()), t, vr), + TypeEntry(name, t, vr), m_qualifiedCppName(name), m_qobject(false), m_polymorphicBase(false), - m_genericClass(false) + m_genericClass(false), + m_deleteInMainThread(false) { } @@ -2578,71 +3415,6 @@ QString ComplexTypeEntry::targetLangApiName() const { return strings_jobject; } -QString StringTypeEntry::targetLangApiName() const -{ - return strings_jobject; -} -QString StringTypeEntry::targetLangName() const -{ - return strings_String; -} -QString StringTypeEntry::targetLangPackage() const -{ - return QString(); -} - -bool StringTypeEntry::isNativeIdBased() const -{ - return false; -} - -CharTypeEntry::CharTypeEntry(const QString &name, const QVersionNumber &vr) : - ValueTypeEntry(name, CharType, vr) -{ - setCodeGeneration(GenerateNothing); -} - -QString CharTypeEntry::targetLangApiName() const -{ - return strings_jchar; -} -QString CharTypeEntry::targetLangName() const -{ - return strings_char; -} - -QString CharTypeEntry::targetLangPackage() const -{ - return QString(); -} - -bool CharTypeEntry::isNativeIdBased() const -{ - return false; -} - -VariantTypeEntry::VariantTypeEntry(const QString &name, const QVersionNumber &vr) : - ValueTypeEntry(name, VariantType, vr) -{ -} - -QString VariantTypeEntry::targetLangApiName() const -{ - return strings_jobject; -} -QString VariantTypeEntry::targetLangName() const -{ - return strings_Object; -} -QString VariantTypeEntry::targetLangPackage() const -{ - return QString(); -} - -bool VariantTypeEntry::isNativeIdBased() const -{ - return false; -} QString ContainerTypeEntry::typeName() const { @@ -2706,10 +3478,6 @@ bool TypeEntry::isCppPrimitive() const 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, const QVersionNumber &vr) : m_name(name), m_type(t), @@ -2719,51 +3487,88 @@ TypeEntry::TypeEntry(const QString &name, TypeEntry::Type t, const QVersionNumbe TypeEntry::~TypeEntry() { - if (typeEntryCustomConversionMap()->contains(this)) { - CustomConversion* customConversion = typeEntryCustomConversionMap()->value(this); - typeEntryCustomConversionMap()->remove(this); - delete customConversion; - } + delete m_customConversion; } bool TypeEntry::hasCustomConversion() const { - return typeEntryCustomConversionMap()->contains(this); + return m_customConversion != nullptr; } + void TypeEntry::setCustomConversion(CustomConversion* customConversion) { - if (customConversion) - typeEntryCustomConversionMap()->insert(this, customConversion); - else if (typeEntryCustomConversionMap()->contains(this)) - typeEntryCustomConversionMap()->remove(this); + m_customConversion = customConversion; } + CustomConversion* TypeEntry::customConversion() const { - if (typeEntryCustomConversionMap()->contains(this)) - return typeEntryCustomConversionMap()->value(this); - return 0; + return m_customConversion; +} + +TypeEntry *TypeEntry::clone() const +{ + return new TypeEntry(*this); +} + +// Take over parameters relevant for typedefs +void TypeEntry::useAsTypedef(const TypeEntry *source) +{ + m_name = source->m_name; + m_targetLangPackage = source->m_targetLangPackage; + m_codeGeneration = source->m_codeGeneration; + m_version = source->m_version; } +TypeEntry::TypeEntry(const TypeEntry &) = default; + TypeSystemTypeEntry::TypeSystemTypeEntry(const QString &name, const QVersionNumber &vr) : TypeEntry(name, TypeSystemType, vr) { } +TypeEntry *TypeSystemTypeEntry::clone() const +{ + return new TypeSystemTypeEntry(*this); +} + +TypeSystemTypeEntry::TypeSystemTypeEntry(const TypeSystemTypeEntry &) = default; + VoidTypeEntry::VoidTypeEntry() : TypeEntry(QLatin1String("void"), VoidType, QVersionNumber(0, 0)) { } +TypeEntry *VoidTypeEntry::clone() const +{ + return new VoidTypeEntry(*this); +} + +VoidTypeEntry::VoidTypeEntry(const VoidTypeEntry &) = default; + VarargsTypeEntry::VarargsTypeEntry() : TypeEntry(QLatin1String("..."), VarargsType, QVersionNumber(0, 0)) { } +TypeEntry *VarargsTypeEntry::clone() const +{ + return new VarargsTypeEntry(*this); +} + +VarargsTypeEntry::VarargsTypeEntry(const VarargsTypeEntry &) = default; + TemplateArgumentEntry::TemplateArgumentEntry(const QString &name, const QVersionNumber &vr) : TypeEntry(name, TemplateArgumentType, vr) { } +TypeEntry *TemplateArgumentEntry::clone() const +{ + return new TemplateArgumentEntry(*this); +} + +TemplateArgumentEntry::TemplateArgumentEntry(const TemplateArgumentEntry &) = default; + ArrayTypeEntry::ArrayTypeEntry(const TypeEntry *nested_type, const QVersionNumber &vr) : TypeEntry(QLatin1String("Array"), ArrayType, vr), m_nestedType(nested_type) @@ -2778,12 +3583,18 @@ QString ArrayTypeEntry::targetLangName() const QString ArrayTypeEntry::targetLangApiName() const { - if (m_nestedType->isPrimitive()) - return m_nestedType->targetLangApiName() + QLatin1String("Array"); - else - return QLatin1String("jobjectArray"); + return m_nestedType->isPrimitive() + ? m_nestedType->targetLangApiName() + QLatin1String("Array") + : QLatin1String("jobjectArray"); } +TypeEntry *ArrayTypeEntry::clone() const +{ + return new ArrayTypeEntry(*this); +} + +ArrayTypeEntry::ArrayTypeEntry(const ArrayTypeEntry &) = default; + EnumTypeEntry::EnumTypeEntry(const QString &nspace, const QString &enumName, const QVersionNumber &vr) : TypeEntry(nspace.isEmpty() ? enumName : nspace + QLatin1String("::") + enumName, @@ -2793,16 +3604,6 @@ EnumTypeEntry::EnumTypeEntry(const QString &nspace, const QString &enumName, { } -QString EnumTypeEntry::targetLangPackage() const -{ - return m_packageName; -} - -void EnumTypeEntry::setTargetLangPackage(const QString &package) -{ - m_packageName = package; -} - QString EnumTypeEntry::targetLangName() const { return m_targetLangName; @@ -2817,11 +3618,34 @@ EnumValueTypeEntry::EnumValueTypeEntry(const QString &name, const QString &value { } +TypeEntry *EnumValueTypeEntry::clone() const +{ + return new EnumValueTypeEntry(*this); +} + +EnumValueTypeEntry::EnumValueTypeEntry(const EnumValueTypeEntry &) = default; + FlagsTypeEntry::FlagsTypeEntry(const QString &name, const QVersionNumber &vr) : TypeEntry(name, FlagsType, vr) { } +/* A typedef entry allows for specifying template specializations in the + * typesystem XML file. */ +TypedefEntry::TypedefEntry(const QString &name, const QString &sourceType, + const QVersionNumber &vr) : + ComplexTypeEntry(name, TypedefType, vr), + m_sourceType(sourceType) +{ +} + +TypeEntry *TypedefEntry::clone() const +{ + return new TypedefEntry(*this); +} + +TypedefEntry::TypedefEntry(const TypedefEntry &) = default; + ContainerTypeEntry::ContainerTypeEntry(const QString &name, Type type, const QVersionNumber &vr) : ComplexTypeEntry(name, ContainerType, vr), @@ -2842,11 +3666,25 @@ SmartPointerTypeEntry::SmartPointerTypeEntry(const QString &name, { } +TypeEntry *SmartPointerTypeEntry::clone() const +{ + return new SmartPointerTypeEntry(*this); +} + +SmartPointerTypeEntry::SmartPointerTypeEntry(const SmartPointerTypeEntry &) = default; + NamespaceTypeEntry::NamespaceTypeEntry(const QString &name, const QVersionNumber &vr) : ComplexTypeEntry(name, NamespaceType, vr) { } +TypeEntry *NamespaceTypeEntry::clone() const +{ + return new NamespaceTypeEntry(*this); +} + +NamespaceTypeEntry::NamespaceTypeEntry(const NamespaceTypeEntry &) = default; + ValueTypeEntry::ValueTypeEntry(const QString &name, const QVersionNumber &vr) : ComplexTypeEntry(name, BasicValueType, vr) { @@ -2862,35 +3700,17 @@ bool ValueTypeEntry::isNativeIdBased() const return true; } -ValueTypeEntry::ValueTypeEntry(const QString &name, Type t, const QVersionNumber &vr) : - ComplexTypeEntry(name, t, vr) +TypeEntry *ValueTypeEntry::clone() const { + return new ValueTypeEntry(*this); } -StringTypeEntry::StringTypeEntry(const QString &name, const QVersionNumber &vr) : - ValueTypeEntry(name, StringType, vr) -{ - setCodeGeneration(GenerateNothing); -} +ValueTypeEntry::ValueTypeEntry(const ValueTypeEntry &) = default; -/* -static void injectCode(ComplexTypeEntry *e, - const char *signature, - const QByteArray &code, - const ArgumentMap &args) +ValueTypeEntry::ValueTypeEntry(const QString &name, Type t, const QVersionNumber &vr) : + ComplexTypeEntry(name, t, vr) { - CodeSnip snip; - snip.language = TypeSystem::NativeCode; - snip.position = CodeSnip::Beginning; - snip.addCode(QString::fromLatin1(code)); - snip.argumentMap = args; - - FunctionModification mod; - mod.signature = QMetaObject::normalizedSignature(signature); - mod.snips << snip; - mod.modifiers = Modification::CodeInjection; } -*/ struct CustomConversion::CustomConversionPrivate { @@ -3042,6 +3862,13 @@ QString InterfaceTypeEntry::qualifiedCppName() const return ComplexTypeEntry::qualifiedCppName().left(len); } +TypeEntry *InterfaceTypeEntry::clone() const +{ + return new InterfaceTypeEntry(*this); +} + +InterfaceTypeEntry::InterfaceTypeEntry(const InterfaceTypeEntry &) = default; + FunctionTypeEntry::FunctionTypeEntry(const QString &name, const QString &signature, const QVersionNumber &vr) : TypeEntry(name, FunctionType, vr) @@ -3049,6 +3876,13 @@ FunctionTypeEntry::FunctionTypeEntry(const QString &name, const QString &signatu addSignature(signature); } +TypeEntry *FunctionTypeEntry::clone() const +{ + return new FunctionTypeEntry(*this); +} + +FunctionTypeEntry::FunctionTypeEntry(const FunctionTypeEntry &) = default; + ObjectTypeEntry::ObjectTypeEntry(const QString &name, const QVersionNumber &vr) : ComplexTypeEntry(name, ObjectType, vr) { @@ -3063,3 +3897,10 @@ bool ObjectTypeEntry::isNativeIdBased() const { return true; } + +TypeEntry *ObjectTypeEntry::clone() const +{ + return new ObjectTypeEntry(*this); +} + +ObjectTypeEntry::ObjectTypeEntry(const ObjectTypeEntry &) = default; diff --git a/sources/shiboken2/ApiExtractor/typesystem.h b/sources/shiboken2/ApiExtractor/typesystem.h index 186c4b24d..b0144923a 100644 --- a/sources/shiboken2/ApiExtractor/typesystem.h +++ b/sources/shiboken2/ApiExtractor/typesystem.h @@ -43,8 +43,8 @@ #include <QtCore/QVersionNumber> //Used to identify the conversion rule to avoid break API -#define TARGET_CONVERSION_RULE_FLAG "0" -#define NATIVE_CONVERSION_RULE_FLAG "1" +extern const char *TARGET_CONVERSION_RULE_FLAG; +extern const char *NATIVE_CONVERSION_RULE_FLAG; class Indentor; @@ -203,12 +203,6 @@ struct ArgumentModification QString replace_value; - // The code to be used to construct a return value when noNullPointers is true and - // the returned value is null. If noNullPointers is true and this string is - // empty, then the base class implementation will be used (or a default construction - // if there is no implementation) - QString nullPointerDefaultValue; - // The text of the new default expression of the argument QString replacedDefaultExpression; @@ -237,6 +231,7 @@ struct ArgumentModification struct Modification { enum Modifiers { + InvalidModifier = 0x0000, Private = 0x0001, Protected = 0x0002, Public = 0x0003, @@ -253,8 +248,7 @@ struct Modification CodeInjection = 0x1000, Rename = 0x2000, Deprecated = 0x4000, - ReplaceExpression = 0x8000, - VirtualSlot = 0x10000 | NonFinal + ReplaceExpression = 0x8000 }; bool isAccessModifier() const @@ -289,10 +283,6 @@ struct Modification { return modifiers & NonFinal; } - bool isVirtualSlot() const - { - return (modifiers & VirtualSlot) == VirtualSlot; - } QString accessModifierString() const; bool isDeprecated() const @@ -318,6 +308,10 @@ struct Modification return removal != TypeSystem::NoLanguage; } +#ifndef QT_NO_DEBUG_STREAM + void formatDebug(QDebug &d) const; +#endif + QString renamedToName; uint modifiers = 0; TypeSystem::Language removal = TypeSystem::NoLanguage; @@ -325,6 +319,8 @@ struct Modification struct FunctionModification: public Modification { + using AllowThread = TypeSystem::AllowThread; + bool isCodeInjection() const { return modifiers & CodeInjection; @@ -337,14 +333,9 @@ struct FunctionModification: public Modification { return m_thread; } - bool allowThread() const - { - return m_allowThread; - } - void setAllowThread(bool allow) - { - m_allowThread = allow; - } + + AllowThread allowThread() const { return m_allowThread; } + void setAllowThread(AllowThread allow) { m_allowThread = allow; } bool matches(const QString &functionSignature) const { @@ -359,8 +350,15 @@ struct FunctionModification: public Modification void setOriginalSignature(const QString &s) { m_originalSignature = s; } QString originalSignature() const { return m_originalSignature; } + TypeSystem::ExceptionHandling exceptionHandling() const { return m_exceptionHandling; } + void setExceptionHandling(TypeSystem::ExceptionHandling e) { m_exceptionHandling = e; } + QString toString() const; +#ifndef QT_NO_DEBUG_STREAM + void formatDebug(QDebug &d) const; +#endif + QString association; CodeSnipList snips; @@ -371,9 +369,17 @@ private: QString m_originalSignature; QRegularExpression m_signaturePattern; bool m_thread = false; - bool m_allowThread = false; + AllowThread m_allowThread = AllowThread::Unspecified; + TypeSystem::ExceptionHandling m_exceptionHandling = TypeSystem::ExceptionHandling::Unspecified; }; +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug d, const ReferenceCount &); +QDebug operator<<(QDebug d, const ArgumentOwner &a); +QDebug operator<<(QDebug d, const ArgumentModification &a); +QDebug operator<<(QDebug d, const FunctionModification &fm); +#endif + struct FieldModification: public Modification { bool isReadable() const @@ -398,6 +404,7 @@ struct AddedFunction { /// Function access types. enum Access { + InvalidAccess = 0, Protected = 0x1, Public = 0x2 }; @@ -532,7 +539,6 @@ class CustomConversion; class TypeEntry { - Q_DISABLE_COPY(TypeEntry) Q_GADGET public: enum Type { @@ -558,7 +564,8 @@ public: CustomType, TargetLangType, FunctionType, - SmartPointerType + SmartPointerType, + TypedefType }; Q_ENUM(Type) @@ -670,15 +677,6 @@ public: return m_type == EnumValue; } - virtual bool preferredConversion() const - { - return m_preferredConversion; - } - virtual void setPreferredConversion(bool b) - { - m_preferredConversion = b; - } - bool stream() const { return m_stream; @@ -715,6 +713,11 @@ public: && m_codeGeneration != TypeEntry::GenerateNothing; } + int revision() const { return m_revision; } + void setRevision(int r); // see typedatabase.cpp + int sbkIndex() const; + void setSbkIndex(int i) { m_sbkIndex = i; } + virtual QString qualifiedCppName() const { return m_name; @@ -747,10 +750,8 @@ public: } // The package - virtual QString targetLangPackage() const - { - return QString(); - } + QString targetLangPackage() const { return m_targetLangPackage; } + void setTargetLangPackage(const QString &p) { m_targetLangPackage = p; } virtual QString qualifiedTargetLangName() const { @@ -889,13 +890,29 @@ public: bool hasCustomConversion() const; void setCustomConversion(CustomConversion* customConversion); CustomConversion* customConversion() const; + + virtual TypeEntry *clone() const; + + void useAsTypedef(const TypeEntry *source); + +#ifndef QT_NO_DEBUG_STREAM + virtual void formatDebug(QDebug &d) const; +#endif + +protected: + TypeEntry(const TypeEntry &); + private: + TypeEntry &operator=(const TypeEntry &) = delete; + TypeEntry &operator=(TypeEntry &&) = delete; + TypeEntry(TypeEntry &&) = delete; + QString m_name; + QString m_targetLangPackage; Type m_type; uint m_codeGeneration = GenerateAll; CustomFunction m_customConstructor; CustomFunction m_customDestructor; - bool m_preferredConversion = true; CodeSnipList m_codeSnips; DocModificationList m_docModifications; IncludeList m_extraIncludes; @@ -904,24 +921,42 @@ private: QString m_conversionRule; bool m_stream = false; QVersionNumber m_version; + CustomConversion *m_customConversion = nullptr; + int m_revision = 0; + int m_sbkIndex = 0; }; class TypeSystemTypeEntry : public TypeEntry { public: explicit TypeSystemTypeEntry(const QString &name, const QVersionNumber &vr); + + TypeEntry *clone() const override; + +protected: + TypeSystemTypeEntry(const TypeSystemTypeEntry &); }; class VoidTypeEntry : public TypeEntry { public: VoidTypeEntry(); + + TypeEntry *clone() const override; + +protected: + VoidTypeEntry(const VoidTypeEntry &); }; class VarargsTypeEntry : public TypeEntry { public: VarargsTypeEntry(); + + TypeEntry *clone() const override; + +protected: + VarargsTypeEntry(const VarargsTypeEntry &); }; class TemplateArgumentEntry : public TypeEntry @@ -938,6 +973,11 @@ public: m_ordinal = o; } + TypeEntry *clone() const override; + +protected: + TemplateArgumentEntry(const TemplateArgumentEntry &); + private: int m_ordinal = 0; }; @@ -959,6 +999,11 @@ public: QString targetLangName() const override; QString targetLangApiName() const override; + TypeEntry *clone() const override; + +protected: + ArrayTypeEntry(const ArrayTypeEntry &); + private: const TypeEntry *m_nestedType; }; @@ -1019,9 +1064,6 @@ public: */ PrimitiveTypeEntry* basicReferencedTypeEntry() const; - bool preferredConversion() const override; - void setPreferredConversion(bool b) override; - bool preferredTargetLangType() const { return m_preferredTargetLangType; @@ -1031,26 +1073,27 @@ public: m_preferredTargetLangType = b; } - void setTargetLangPackage(const QString& package); - QString targetLangPackage() const override; + TypeEntry *clone() const override; + +protected: + PrimitiveTypeEntry(const PrimitiveTypeEntry &); + private: QString m_targetLangName; QString m_targetLangApiName; QString m_defaultConstructor; - uint m_preferredConversion : 1; uint m_preferredTargetLangType : 1; PrimitiveTypeEntry* m_referencedTypeEntry = nullptr; }; +class EnumValueTypeEntry; + class EnumTypeEntry : public TypeEntry { public: explicit EnumTypeEntry(const QString &nspace, const QString &enumName, const QVersionNumber &vr); - QString targetLangPackage() const override; - void setTargetLangPackage(const QString &package); - QString targetLangName() const override; QString targetLangQualifier() const; QString qualifiedTargetLangName() const override; @@ -1066,30 +1109,8 @@ public: m_qualifier = q; } - bool preferredConversion() const override; - - bool isBoundsChecked() const - { - return m_lowerBound.isEmpty() && m_upperBound.isEmpty(); - } - - QString upperBound() const - { - return m_upperBound; - } - void setUpperBound(const QString &bound) - { - m_upperBound = bound; - } - - QString lowerBound() const - { - return m_lowerBound; - } - void setLowerBound(const QString &bound) - { - m_lowerBound = bound; - } + const EnumValueTypeEntry *nullValue() const { return m_nullValue; } + void setNullValue(const EnumValueTypeEntry *n) { m_nullValue = n; } void setFlags(FlagsTypeEntry *flags) { @@ -1100,15 +1121,6 @@ public: return m_flags; } - bool isExtensible() const - { - return m_extensible; - } - void setExtensible(bool is) - { - m_extensible = is; - } - bool isEnumValueRejected(const QString &name) const { return m_rejectedEnums.contains(name); @@ -1122,33 +1134,27 @@ public: return m_rejectedEnums; } - bool forceInteger() const - { - return m_forceInteger; - } - void setForceInteger(bool force) - { - m_forceInteger = force; - } + TypeEntry *clone() const override; +#ifndef QT_NO_DEBUG_STREAM + void formatDebug(QDebug &d) const override; +#endif +protected: + EnumTypeEntry(const EnumTypeEntry &); private: QString m_packageName; QString m_qualifier; QString m_targetLangName; - - QString m_lowerBound; - QString m_upperBound; + const EnumValueTypeEntry *m_nullValue = nullptr; QStringList m_rejectedEnums; FlagsTypeEntry *m_flags = nullptr; - - bool m_extensible = false; - bool m_forceInteger = false; }; // EnumValueTypeEntry is used for resolving integer type templates -// like array<EnumValue>. +// like array<EnumValue>. Note: Dummy entries for integer values will +// be created for non-type template parameters, where m_enclosingEnum==nullptr. class EnumValueTypeEntry : public TypeEntry { public: @@ -1156,6 +1162,12 @@ public: QString value() const { return m_value; } const EnumTypeEntry* enclosingEnum() const { return m_enclosingEnum; } + + TypeEntry *clone() const override; + +protected: + EnumValueTypeEntry(const EnumValueTypeEntry &); + private: QString m_value; const EnumTypeEntry* m_enclosingEnum; @@ -1169,7 +1181,6 @@ public: QString qualifiedTargetLangName() const override; QString targetLangName() const override; QString targetLangApiName() const override; - bool preferredConversion() const override; QString originalName() const { @@ -1189,11 +1200,6 @@ public: m_targetLangName = name; } - bool forceInteger() const - { - return m_enum->forceInteger(); - } - EnumTypeEntry *originator() const { return m_enum; @@ -1203,7 +1209,10 @@ public: m_enum = e; } - QString targetLangPackage() const override; + TypeEntry *clone() const override; + +protected: + FlagsTypeEntry(const FlagsTypeEntry &); private: QString m_originalName; @@ -1216,8 +1225,6 @@ class ComplexTypeEntry : public TypeEntry { public: enum TypeFlag { - ForceAbstract = 0x1, - DeleteInMainThread = 0x2, Deprecated = 0x4 }; typedef QFlags<TypeFlag> TypeFlags; @@ -1288,12 +1295,6 @@ public: return m_fieldMods; } - QString targetLangPackage() const override; - void setTargetLangPackage(const QString &package) - { - m_package = package; - } - bool isQObject() const { return m_qobject; @@ -1336,15 +1337,6 @@ public: return m_polymorphicIdValue; } - void setHeldType(const QString &value) - { - m_heldTypeValue = value; - } - QString heldTypeValue() const - { - return m_heldTypeValue; - } - QString targetType() const { return m_targetType; @@ -1369,6 +1361,9 @@ public: m_genericClass = isGeneric; } + bool deleteInMainThread() const { return m_deleteInMainThread; } + void setDeleteInMainThread(bool d) { m_deleteInMainThread = d; } + CopyableFlag copyable() const { return m_copyableFlag; @@ -1397,15 +1392,28 @@ public: return m_baseContainerType; } + TypeSystem::ExceptionHandling exceptionHandling() const { return m_exceptionHandling; } + void setExceptionHandling(TypeSystem::ExceptionHandling e) { m_exceptionHandling = e; } + QString defaultConstructor() const; void setDefaultConstructor(const QString& defaultConstructor); bool hasDefaultConstructor() const; + TypeEntry *clone() const override; + + void useAsTypedef(const ComplexTypeEntry *source); + +#ifndef QT_NO_DEBUG_STREAM + void formatDebug(QDebug &d) const override; +#endif +protected: + ComplexTypeEntry(const ComplexTypeEntry &); + private: AddedFunctionList m_addedFunctions; FunctionModificationList m_functionMods; FieldModificationList m_fieldMods; - QString m_package; + QString m_defaultConstructor; QString m_defaultSuperclass; QString m_qualifiedCppName; QString m_targetLangName; @@ -1413,9 +1421,9 @@ private: uint m_qobject : 1; uint m_polymorphicBase : 1; uint m_genericClass : 1; + uint m_deleteInMainThread : 1; QString m_polymorphicIdValue; - QString m_heldTypeValue; QString m_lookupName; QString m_targetType; TypeFlags m_typeFlags; @@ -1423,6 +1431,38 @@ private: QString m_hashFunction; const ComplexTypeEntry* m_baseContainerType = nullptr; + // For class functions + TypeSystem::ExceptionHandling m_exceptionHandling = TypeSystem::ExceptionHandling::Unspecified; +}; + +class TypedefEntry : public ComplexTypeEntry +{ +public: + explicit TypedefEntry(const QString &name, + const QString &sourceType, + const QVersionNumber &vr); + + QString sourceType() const { return m_sourceType; } + void setSourceType(const QString &s) { m_sourceType =s; } + + TypeEntry *clone() const override; + + ComplexTypeEntry *source() const { return m_source; } + void setSource(ComplexTypeEntry *source) { m_source = source; } + + ComplexTypeEntry *target() const { return m_target; } + void setTarget(ComplexTypeEntry *target) { m_target = target; } + +#ifndef QT_NO_DEBUG_STREAM + virtual void formatDebug(QDebug &d) const override; +#endif +protected: + TypedefEntry(const TypedefEntry &); + +private: + QString m_sourceType; + ComplexTypeEntry *m_source = nullptr; + ComplexTypeEntry *m_target = nullptr; }; class ContainerTypeEntry : public ComplexTypeEntry @@ -1455,28 +1495,15 @@ public: QString typeName() const; QString targetLangName() const override; - QString targetLangPackage() const override; QString qualifiedCppName() const override; - static Type containerTypeFromString(QString typeName) - { - static QHash<QString, Type> m_stringToContainerType; - if (m_stringToContainerType.isEmpty()) { - m_stringToContainerType.insert(QLatin1String("list"), ListContainer); - m_stringToContainerType.insert(QLatin1String("string-list"), StringListContainer); - m_stringToContainerType.insert(QLatin1String("linked-list"), LinkedListContainer); - m_stringToContainerType.insert(QLatin1String("vector"), VectorContainer); - m_stringToContainerType.insert(QLatin1String("stack"), StackContainer); - m_stringToContainerType.insert(QLatin1String("queue"), QueueContainer); - m_stringToContainerType.insert(QLatin1String("set"), SetContainer); - m_stringToContainerType.insert(QLatin1String("map"), MapContainer); - m_stringToContainerType.insert(QLatin1String("multi-map"), MultiMapContainer); - m_stringToContainerType.insert(QLatin1String("hash"), HashContainer); - m_stringToContainerType.insert(QLatin1String("multi-hash"), MultiHashContainer); - m_stringToContainerType.insert(QLatin1String("pair"), PairContainer); - } - return m_stringToContainerType.value(typeName, NoContainer); - } + TypeEntry *clone() const override; + +#ifndef QT_NO_DEBUG_STREAM + void formatDebug(QDebug &d) const override; +#endif +protected: + ContainerTypeEntry(const ContainerTypeEntry &); private: Type m_type; @@ -1501,6 +1528,11 @@ public: return m_refCountMethodName; } + TypeEntry *clone() const override; + +protected: + SmartPointerTypeEntry(const SmartPointerTypeEntry &); + private: QString m_getterName; QString m_smartPointerType; @@ -1511,6 +1543,11 @@ class NamespaceTypeEntry : public ComplexTypeEntry { public: explicit NamespaceTypeEntry(const QString &name, const QVersionNumber &vr); + + TypeEntry *clone() const override; + +protected: + NamespaceTypeEntry(const NamespaceTypeEntry &); }; @@ -1523,48 +1560,13 @@ public: bool isNativeIdBased() const override; + TypeEntry *clone() const override; + protected: explicit ValueTypeEntry(const QString &name, Type t, const QVersionNumber &vr); + ValueTypeEntry(const ValueTypeEntry &); }; - -class StringTypeEntry : public ValueTypeEntry -{ -public: - explicit StringTypeEntry(const QString &name, const QVersionNumber &vr); - - QString targetLangApiName() const override; - QString targetLangName() const override; - QString targetLangPackage() const override; - - bool isNativeIdBased() const override; -}; - -class CharTypeEntry : public ValueTypeEntry -{ -public: - explicit CharTypeEntry(const QString &name, const QVersionNumber &vr); - - QString targetLangApiName() const override; - QString targetLangName() const override; - QString targetLangPackage() const override; - - bool isNativeIdBased() const override; -}; - -class VariantTypeEntry: public ValueTypeEntry -{ -public: - explicit VariantTypeEntry(const QString &name, const QVersionNumber &vr); - - QString targetLangApiName() const override; - QString targetLangName() const override; - QString targetLangPackage() const override; - - bool isNativeIdBased() const override; -}; - - class InterfaceTypeEntry : public ComplexTypeEntry { public: @@ -1587,6 +1589,11 @@ public: bool isNativeIdBased() const override; QString qualifiedCppName() const override; + TypeEntry *clone() const override; + +protected: + InterfaceTypeEntry(const InterfaceTypeEntry &); + private: ObjectTypeEntry *m_origin; }; @@ -1611,6 +1618,12 @@ public: { return m_signatures.contains(signature); } + + TypeEntry *clone() const override; + +protected: + FunctionTypeEntry(const FunctionTypeEntry &); + private: QStringList m_signatures; }; @@ -1628,6 +1641,11 @@ public: bool isNativeIdBased() const override; + TypeEntry *clone() const override; + +protected: + ObjectTypeEntry(const ObjectTypeEntry &); + private: InterfaceTypeEntry *m_interface = nullptr; }; @@ -1641,7 +1659,8 @@ struct TypeRejection Field, // Match className and field name Enum, // Match className and enum name ArgumentType, // Match className and argument type - ReturnType // Match className and return type + ReturnType, // Match className and return type + Invalid }; QRegularExpression className; diff --git a/sources/shiboken2/ApiExtractor/typesystem_enums.h b/sources/shiboken2/ApiExtractor/typesystem_enums.h index 6bfc94368..df83429d0 100644 --- a/sources/shiboken2/ApiExtractor/typesystem_enums.h +++ b/sources/shiboken2/ApiExtractor/typesystem_enums.h @@ -55,6 +55,13 @@ enum Language { TargetLangAndNativeCode = TargetLangCode | NativeCode }; +enum class AllowThread { + Allow, + Disallow, + Auto, + Unspecified +}; + enum Ownership { InvalidOwnership, DefaultOwnership, @@ -71,14 +78,24 @@ enum CodeSnipPosition { CodeSnipPositionPrototypeInitialization, CodeSnipPositionConstructorInitialization, CodeSnipPositionConstructor, - CodeSnipPositionAny + CodeSnipPositionAny, + CodeSnipPositionInvalid }; enum DocModificationMode { DocModificationAppend, DocModificationPrepend, DocModificationReplace, - DocModificationXPathReplace + DocModificationXPathReplace, + DocModificationInvalid +}; + +enum class ExceptionHandling { + Unspecified, + Off, + AutoDefaultToOff, + AutoDefaultToOn, + On }; } // namespace TypeSystem diff --git a/sources/shiboken2/ApiExtractor/typesystem_p.h b/sources/shiboken2/ApiExtractor/typesystem_p.h index 882cf3fab..a617110d6 100644 --- a/sources/shiboken2/ApiExtractor/typesystem_p.h +++ b/sources/shiboken2/ApiExtractor/typesystem_p.h @@ -55,6 +55,7 @@ class StackElement FunctionTypeEntry = 0xb, CustomTypeEntry = 0xc, SmartPointerTypeEntry = 0xd, + TypedefTypeEntry = 0xe, TypeEntryMask = 0xf, // Documentation tags @@ -140,37 +141,117 @@ public: bool parse(QXmlStreamReader &reader); + QString errorString() const { return m_error; } + private: - bool startElement(const QStringRef& localName, const QXmlStreamAttributes& atts); - bool handleSmartPointerEntry(StackElement *element, - QHash<QString, QString> &attributes, - const QString &name, - const QVersionNumber &since); + bool startElement(const QXmlStreamReader &reader); + SmartPointerTypeEntry *parseSmartPointerEntry(const QXmlStreamReader &, + const QString &name, + const QVersionNumber &since, + QXmlStreamAttributes *attributes); bool endElement(const QStringRef& localName); template <class String> // QString/QStringRef bool characters(const String &ch); - void fetchAttributeValues(const QString &name, const QXmlStreamAttributes &atts, - QHash<QString, QString> *acceptedAttributes); bool importFileElement(const QXmlStreamAttributes &atts); - void addFlags(const QString &name, QString flagName, - const QHash<QString, QString> &attributes, - const QVersionNumber &since); + + 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 *); + 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; - StackElement* m_currentDroppedEntry; - int m_currentDroppedEntryDepth; - int m_ignoreDepth; + 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; QString m_error; - TypeEntry::CodeGeneration m_generate; + const TypeEntry::CodeGeneration m_generate; - EnumTypeEntry* m_currentEnum; + EnumTypeEntry* m_currentEnum = nullptr; QStack<StackElementContext*> m_contextStack; - QHash<QString, StackElement::ElementType> tagNames; QString m_currentSignature; QString m_currentPath; }; diff --git a/sources/shiboken2/CMakeLists.txt b/sources/shiboken2/CMakeLists.txt index 1af84fca1..619c0f08c 100644 --- a/sources/shiboken2/CMakeLists.txt +++ b/sources/shiboken2/CMakeLists.txt @@ -5,8 +5,9 @@ include(CheckIncludeFileCXX) cmake_minimum_required(VERSION 3.1) cmake_policy(VERSION 3.1) -set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules/ +set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/../cmake_helpers/ ${CMAKE_MODULE_PATH}) +include(helpers) find_package(Qt5 5.7 REQUIRED COMPONENTS Core Xml XmlPatterns) @@ -15,6 +16,25 @@ add_definitions(${Qt5Core_DEFINITIONS}) option(BUILD_TESTS "Build tests." TRUE) option(USE_PYTHON_VERSION "Use specific python version to build shiboken2." "") +# Don't display "up-to-date / install" messages when installing, to reduce visual clutter. +if (QUIET_BUILD) + set(CMAKE_INSTALL_MESSAGE NEVER) +endif() + +# Override message not to display info messages when doing a quiet build. +if (QUIET_BUILD) + function(message) + list(GET ARGV 0 MessageType) + if (MessageType STREQUAL FATAL_ERROR OR + MessageType STREQUAL SEND_ERROR OR + MessageType STREQUAL WARNING OR + MessageType STREQUAL AUTHOR_WARNING) + list(REMOVE_AT ARGV 0) + _message(${MessageType} "${ARGV}") + endif() + endfunction() +endif() + if (USE_PYTHON_VERSION) find_package(PythonInterp ${USE_PYTHON_VERSION} REQUIRED) find_package(PythonLibs ${USE_PYTHON_VERSION} REQUIRED) @@ -158,6 +178,8 @@ list(GET SHIBOKEN_VERSION_OUTPUT 4 shiboken_PRE_RELEASE_VERSION) set(shiboken2_VERSION "${shiboken_MAJOR_VERSION}.${shiboken_MINOR_VERSION}.${shiboken_MICRO_VERSION}") set(shiboken2_library_so_version "${shiboken_MAJOR_VERSION}.${shiboken_MINOR_VERSION}") +compute_config_py_values(shiboken2_VERSION) + ## For debugging the PYTHON* variables message("PYTHONLIBS_FOUND: " ${PYTHONLIBS_FOUND}) message("PYTHON_LIBRARIES: " ${PYTHON_LIBRARIES}) diff --git a/sources/shiboken2/data/generatorrunner.1 b/sources/shiboken2/data/generatorrunner.1 index 045b55ad4..f5a61f139 100644 --- a/sources/shiboken2/data/generatorrunner.1 +++ b/sources/shiboken2/data/generatorrunner.1 @@ -13,9 +13,9 @@ are two generators available: .B qtdoc \- Generates Sphinx-based documentation for C++ libraries documented using -.B qdoc3 +.B qdoc documentation syntax, using the XML files created by the documentation tool -.B (qdoc3). +.B (qdoc). Can be called supplying .B \-\-generator-set=qtdoc to @@ -65,7 +65,7 @@ Drops support for named args. .IP \-\-documentation\-code\-snippets\-dir Directory used to search code snippets used by the documentation .IP \-\-documentation\-data\-dir -Directory with XML files generated by documentation tool (qdoc3 or Doxygen) +Directory with XML files generated by documentation tool (qdoc or Doxygen) .IP \-\-documentation\-out\-dir The directory where the generated documentation files will be written .IP \-\-library\-source\-dir diff --git a/sources/shiboken2/doc/commandlineoptions.rst b/sources/shiboken2/doc/commandlineoptions.rst index d373561cd..c335fab75 100644 --- a/sources/shiboken2/doc/commandlineoptions.rst +++ b/sources/shiboken2/doc/commandlineoptions.rst @@ -37,16 +37,22 @@ Options Enable heuristics to detect parent relationship on return values. For more info, check :ref:`return-value-heuristics`. +.. _avoid-protected-hack: + +``--avoid-protected-hack`` + Avoid the use of the '#define protected public' hack. + +.. _use-isnull-as-nb_nonzero: + +``--use-isnull-as-nb_nonzero`` + If a class have an isNull() const method, it will be used to + compute the value of boolean casts + .. _api-version: ``--api-version=<version>`` Specify the supported api version used to generate the bindings. -.. _debug-level: - -``--debug-level=[sparse|medium|full]`` - Set the debug level. - .. _documentation-only: ``--documentation-only`` @@ -63,16 +69,52 @@ Options ``--generation-set`` Generator set to be used (e.g. qtdoc). -.. _help: +.. _diff: -``--help`` - Display this help and exit. +``--diff`` + Print a diff of wrapper files. + +.. _dryrun: + +``--dryrun`` + Dry run, do not generate wrapper files. + +.. _--project-file: + +``--project-file=<file>`` + Text file containing a description of the binding project. + Replaces and overrides command line arguments. .. _include-paths: -``--include-paths=<path>[:<path>:...]`` +``-I<path>, --include-paths=<path>[:<path>:...]`` Include paths used by the C++ parser. +... _system-include-paths: + +``-isystem<path>, --system-include-paths=<path>[:<path>:...]`` + System include paths used by the C++ parser + +.. _framework-include-paths: + +``-F<path>, --framework-include-paths=<path>[:<path>:...]`` + Framework include paths used by the C++ parser + +.. _language-level: + +``--language-level=, -std=<level>`` + C++ Language level (c++11..c++17, default=c++14) + +.. _typesystem-paths: + +``-T<path>, --typesystem-paths=<path>[:<path>:...]`` + Paths used when searching for type system files. + +.. _output-directory: + +``--output-directory=[dir]`` + The directory where the generated files will be written. + .. _license-file=[license-file]: ``--license-file=[license-file]`` @@ -83,23 +125,57 @@ Options ``--no-suppress-warnings`` Show all warnings. -.. _output-directory: - -``--output-directory=[dir]`` - The directory where the generated files will be written. - .. _silent: ``--silent`` Avoid printing any message. -.. _typesystem-paths: +.. _debug-level: -``--typesystem-paths=<path>[:<path>:...]`` - Paths used when searching for type system files. +``--debug-level=[sparse|medium|full]`` + Set the debug level. + +.. _help: + +``--help`` + Display this help and exit. .. _version: ``--version`` Output version information and exit. +QtDocGenerator Options +---------------------- + +.. _doc-parser: + +``--doc-parser=<parser>`` + The documentation parser used to interpret the documentation + input files (qdoc|doxygen). + +.. _documentation-code-snippets-dir: + +``--documentation-code-snippets-dir=<dir>`` + Directory used to search code snippets used by the documentation. + +.. _documentation-data-dir: + +``--documentation-data-dir=<dir>`` + Directory with XML files generated by documentation tool. + +.. _documentation-extra-sections-dir=<dir>: + +``--documentation-extra-sections-dir=<dir>`` + Directory used to search for extra documentation sections. + +.. _library-source-dir: + +``--library-source-dir=<dir>`` + Directory where library source code is located. + +.. _additional-documentation: + +``--additional-documentation=<file>`` + List of additional XML files to be converted to .rst files + (for example, tutorials). diff --git a/sources/shiboken2/doc/conf.py.in b/sources/shiboken2/doc/conf.py.in index 57c2f94b9..d3aa95c0b 100644 --- a/sources/shiboken2/doc/conf.py.in +++ b/sources/shiboken2/doc/conf.py.in @@ -39,11 +39,11 @@ source_suffix = '.rst' source_encoding = 'utf-8' # The master toctree document. -#master_doc = 'contents' +master_doc = 'index' # General information about the project. project = u'Shiboken' -copyright = u'Copyright (C) 2016 The Qt Company Ltd.' +copyright = u'© 2018 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the <a href="http://www.gnu.org/license/fdl.html">GNU Free Documentation License version 1.3</a> as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -137,7 +137,7 @@ html_theme_path = ['@CMAKE_CURRENT_SOURCE_DIR@/_themes'] # Additional templates that should be rendered to pages, maps page names to # template names. -html_additional_pages = { 'index' : 'index.html'} +#html_additional_pages = { 'index' : 'index.html'} # If false, no module index is generated. html_use_modindex = False @@ -159,4 +159,4 @@ html_show_sourcelink = False # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = '' -intersphinx_mapping = {'apiextractor': ('@CMAKE_BINARY_DIR@/ApiExtractor/doc/html','@CMAKE_BINARY_DIR@/ApiExtractor/doc/html/objects.inv')} +intersphinx_mapping = {'apiextractor': ('ApiExtractor','@CMAKE_BINARY_DIR@/ApiExtractor/doc/html/objects.inv')} diff --git a/sources/shiboken2/doc/contents.rst b/sources/shiboken2/doc/contents.rst index 24adb1c68..a0b40effb 100644 --- a/sources/shiboken2/doc/contents.rst +++ b/sources/shiboken2/doc/contents.rst @@ -1,11 +1,10 @@ Table of contents ***************** .. toctree:: - :numbered: :maxdepth: 3 - faq.rst overview.rst + samplebinding.rst commandlineoptions.rst projectfile.rst typesystemvariables.rst @@ -15,3 +14,4 @@ Table of contents ownership.rst wordsofadvice.rst shibokenmodule.rst + faq.rst diff --git a/sources/shiboken2/doc/images/icecream.png b/sources/shiboken2/doc/images/icecream.png Binary files differnew file mode 100644 index 000000000..41d1a25fa --- /dev/null +++ b/sources/shiboken2/doc/images/icecream.png diff --git a/sources/shiboken2/doc/images/qtforpython-underthehood.png b/sources/shiboken2/doc/images/qtforpython-underthehood.png Binary files differnew file mode 100644 index 000000000..64e30b1c5 --- /dev/null +++ b/sources/shiboken2/doc/images/qtforpython-underthehood.png diff --git a/sources/shiboken2/doc/index.rst b/sources/shiboken2/doc/index.rst new file mode 100644 index 000000000..4cc5b204e --- /dev/null +++ b/sources/shiboken2/doc/index.rst @@ -0,0 +1,28 @@ +Shiboken the Binding Generator +******************************* + +Shiboken is the CPython-based binding code generator for C or C++ libraries. +It uses an ApiExtractor library to parse the C or C++ headers and get the +type information, using Clang. The library can also be used to parse non-Qt +projects. The following diagram summarizes Shiboken's role in the PySide +project. + +.. image:: images/qtforpython-underthehood.png + +A typesystem file (XML) is used to specify the types to be exposed to Python +and to apply modifications to properly represent and manipulate the types in +the Python World. For example, you can remove and add methods to certain types, +and also modify the arguments of each method. Such actions are inevitable to +properly handle the data structures or types. + +The final outcome of this process is a set of wrappers written in CPython, +which can be used as a module in your python code. + +Refer to the following topics for more information and examples: + +.. toctree:: + :maxdepth: 1 + + overview + samplebinding + contents diff --git a/sources/shiboken2/doc/samplebinding.rst b/sources/shiboken2/doc/samplebinding.rst new file mode 100644 index 000000000..be8dd3ae5 --- /dev/null +++ b/sources/shiboken2/doc/samplebinding.rst @@ -0,0 +1,250 @@ +SampleBinding Example +*********************** + +The example showcases how you can generate CPython-based binding code for a +C++ library using Shiboken. The C++ library is called :code:`Universe`, +with two classes: :code:`Icecream` and :code:`Truck`. Ice creams are +characterized by their flavor, and :code:`Truck` serves as a vehicle of +:code:`Icecream` distribution for kids in a neighborhood. + +First, let's look at the definition of the two classes: + +.. code-block:: cpp + :caption: icecream.h + + class Icecream + { + public: + Icecream(const std::string &flavor); + virtual Icecream *clone(); + virtual ~Icecream(); + virtual const std::string getFlavor(); + + private: + std::string m_flavor; + }; + +.. code-block:: cpp + :caption: truck.h + + class Truck { + public: + Truck(bool leaveOnDestruction = false); + Truck(const Truck &other); + Truck& operator=(const Truck &other); + ~Truck(); + + void addIcecreamFlavor(Icecream *icecream); + void printAvailableFlavors() const; + + bool deliver() const; + void arrive() const; + void leave() const; + + void setLeaveOnDestruction(bool value); + void setArrivalMessage(const std::string &message); + + private: + void clearFlavors(); + + bool m_leaveOnDestruction = false; + std::string m_arrivalMessage = "A new icecream truck has arrived!\n"; + std::vector m_flavors; + }; + +Here is a summary of what the :code:`Universe` library includes: + +* The :code:`Icecream` polymorphic type, which is intended to be overridden. +* The :code:`Icecream::getFlavor()` method returns the flavor depending on the + actual derived type. +* The :code:`Truck` value type that contains pointers, hence the copy + constructor. +* :code:`Truck` stores the :code:`Icecream` objects in a vector, which can be + modified via :code:`Truck::addIcecreamFlavor()`. +* The :code:`Truck’s` arrival message can be customized using its + :code:`setArrivalMessage()` method. +* The :code:`Truck::deliver()` method tells us if the ice cream delivery was + successful. + +Shiboken typesystem +==================== + +Now that the library definitions are in place, Shiboken generator needs a header +file that includes the types we are interested in: + +.. code-block:: cpp + :caption: bindings.h + + #ifndef BINDINGS_H + #define BINDINGS_H + #include "icecream.h" + #include "truck.h" + #endif // BINDINGS_H + +In addition, Shiboken also requires an XML-based typesystem file that defines the +relationship between C++ and Python types: + +.. code-block:: xml + :caption: bindings.xml + + <?xml version="1.0"?> + <typesystem package="Universe"> + <primitive-type name="bool"/> + <primitive-type name="std::string"/> + <object-type name="Icecream"> + <modify-function signature="clone()"> + <modify-argument index="0"> + <define-ownership owner="c++"/> + </modify-argument> + </modify-function> + </object-type> + <value-type name="Truck"> + <modify-function signature="addIcecreamFlavor(Icecream*)"> + <modify-argument index="1"> + <define-ownership owner="c++"/> + </modify-argument> + </modify-function> + </value-type> + </typesystem> + +The first important thing to notice here is that we declare :code:`"bool"` and +:code:`"std::string"` as primitive types. These types are used by some of the +C++ methods as parameters or return types, so Shiboken must know about them. +It can then generate relevant conversion code between C++ and Python, although +most C++ primitive types are handled by Shiboken without additional code. + +Next, we declare the two aforementioned classes. One of them as an +“object-type” and the other as a “value-type”. The main difference is that +object-types are passed around in generated code as pointers, whereas +value-types are copied (value semantics). + +By specifying the names of these classes in the typesystem file, Shiboken +automatically tries to generate bindings for all methods of those +classes. You need not mention all the methods manually in the XML file, unless +you want to modify them. + +Object ownership rules +======================= + +Shiboken cannot magically know who is responsible for freeing the C++ objects +allocated in the Python code. It can guess, but it’s not always correct. There +can be cases where Python should release the C++ memory when the ref count +of the Python object becomes zero. It should never delete the C++ object assuming +that it will not be deleted by the C++ library or maybe it’s parented to another +object (like QWidgets). + +In our case, the :code:`clone()` method is only called inside the C++ library, +and we assume that the C++ code takes care of releasing the cloned object. + +As for :code:`addIcecreamFlavor()`, we know that a :code:`Truck` owns the +:code:`Icecream` object, and will remove it once the :code:`Truck` is +destroyed. That's why the ownership is set to “c++” in the typesystem file, +so that the C++ objects are not deleted when the corresponding Python names +go out of scope. + +Building +========= + +To build the :code:`Universe` custom library and then generate bindings for it, +use the :file:`CMakeLists.txt` file provided with the example. You can reuse the +file for your own libraries with minor changes. + +Now, run the command :command:`"cmake ."` from the prompt to configure the +project and build with the toolchain of your choice (we recommend the +‘(N)Makefiles’ generator though). + +As a result, you end up with two shared libraries: +:file:`libuniverse.(so/dylib/dll)` and :file:`Universe.(so/pyd)`. The former is +the custom C++ library, and the latter is the Python module that can be +imported in your Python script. + +Refer to the :file:`README.md` file for more details about the Windows-specific +build instructions. + +Using the Python module +======================== + +The following script uses the :code:`Universe` module, derives a few types from +:code:`Icecream`, implements virtual methods, instantiates objects, and much more: + +.. code-block:: python + :caption: main.py + + from Universe import Icecream, Truck + + class VanillaChocolateIcecream(Icecream): + def __init__(self, flavor=""): + super(VanillaChocolateIcecream, self).__init__(flavor) + + def clone(self): + return VanillaChocolateIcecream(self.getFlavor()) + + def getFlavor(self): + return "vanilla sprinked with chocolate" + + class VanillaChocolateCherryIcecream(VanillaChocolateIcecream): + def __init__(self, flavor=""): + super(VanillaChocolateIcecream, self).__init__(flavor) + + def clone(self): + return VanillaChocolateCherryIcecream(self.getFlavor()) + + def getFlavor(self): + base_flavor = super(VanillaChocolateCherryIcecream, self).getFlavor() + return base_flavor + " and a cherry" + + if __name__ == '__main__': + leave_on_destruction = True + truck = Truck(leave_on_destruction) + + flavors = ["vanilla", "chocolate", "strawberry"] + for f in flavors: + icecream = Icecream(f) + truck.addIcecreamFlavor(icecream) + + truck.addIcecreamFlavor(VanillaChocolateIcecream()) + truck.addIcecreamFlavor(VanillaChocolateCherryIcecream()) + + truck.arrive() + truck.printAvailableFlavors() + result = truck.deliver() + + if result: + print("All the kids got some icecream!") + else: + print("Aww, someone didn't get the flavor they wanted...") + + if not result: + special_truck = Truck(truck) + del truck + + print("") + special_truck.setArrivalMessage("A new SPECIAL icecream truck has arrived!\n") + special_truck.arrive() + special_truck.addIcecreamFlavor(Icecream("SPECIAL *magical* icecream")) + special_truck.printAvailableFlavors() + special_truck.deliver() + print("Now everyone got the flavor they wanted!") + special_truck.leave() + +After importing the classes from the :code:`Universe` module, it derives two +types from :code:`Icecream` for different “flavors”. It then creates a +:code:`truck` to deliver some regular flavored Icecreams and two special ones. + +If the delivery fails, a new :code:`truck` is created with the old flavors +copied over, and a new *magical* flavor that will surely satisfy all customers. + +The script above shows how to derive from C++ types, override virtual methods, +create and destroy objects, and more. Try running it to see if the ice creams +are delivered. + +.. note:: + You can find the sources for this example under + :file:`<PYTHON_ENV_ROOT>/site-packages/lib/PySide2/examples/samplebinding`. + +Refer to the following topics for detailed information about using Shiboken: + * :doc:`Shiboken module <shibokenmodule>` + * :doc:`Type System Variables <typesystemvariables>` + * :doc:`User Defined Type Conversion <typeconverters>` + * :doc:`Object ownership <ownership>` + * :doc:`Frequently Asked Questions <faq>` diff --git a/sources/shiboken2/generator/CMakeLists.txt b/sources/shiboken2/generator/CMakeLists.txt index 032118666..fb8058b2d 100644 --- a/sources/shiboken2/generator/CMakeLists.txt +++ b/sources/shiboken2/generator/CMakeLists.txt @@ -38,3 +38,33 @@ target_link_libraries(shiboken2 configure_file(shibokenconfig.h.in "${CMAKE_CURRENT_BINARY_DIR}/shibokenconfig.h" @ONLY) install(TARGETS shiboken2 DESTINATION bin) + +set(shiboken_generator_package_name "shiboken2_generator") + +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/_config.py.in" + "${CMAKE_CURRENT_BINARY_DIR}/_config.py" @ONLY) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/_config.py" + DESTINATION "${PYTHON_SITE_PACKAGES}/${shiboken_generator_package_name}") + +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/__init__.py.in" + "${CMAKE_CURRENT_BINARY_DIR}/__init__.py" @ONLY) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/__init__.py" + DESTINATION "${PYTHON_SITE_PACKAGES}/${shiboken_generator_package_name}") + +# shiboken2 setuptools entry point +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../shiboken_tool.py + DESTINATION bin + PERMISSIONS + OWNER_EXECUTE OWNER_WRITE OWNER_READ + GROUP_EXECUTE GROUP_READ + WORLD_EXECUTE WORLD_READ) + +# Use absolute path instead of relative path, to avoid ninja build errors due to +# duplicate file dependency inconsistency. +set(shiboken_version_relative_path "${CMAKE_CURRENT_SOURCE_DIR}/../shiboken_version.py") +get_filename_component(shiboken_version_path ${shiboken_version_relative_path} ABSOLUTE) +configure_file("${shiboken_version_path}" + "${CMAKE_CURRENT_BINARY_DIR}/_git_shiboken_generator_version.py" @ONLY) + +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/_git_shiboken_generator_version.py" + DESTINATION "${PYTHON_SITE_PACKAGES}/${shiboken_generator_package_name}") diff --git a/sources/shiboken2/generator/__init__.py.in b/sources/shiboken2/generator/__init__.py.in new file mode 100644 index 000000000..4be6a833b --- /dev/null +++ b/sources/shiboken2/generator/__init__.py.in @@ -0,0 +1,2 @@ +__version__ = "@FINAL_PACKAGE_VERSION@" +__version_info__ = (@shiboken_MAJOR_VERSION@, @shiboken_MINOR_VERSION@, @shiboken_MICRO_VERSION@, "@shiboken_PRE_RELEASE_VERSION_TYPE@", "@shiboken_PRE_RELEASE_VERSION@") diff --git a/sources/shiboken2/generator/_config.py.in b/sources/shiboken2/generator/_config.py.in new file mode 100644 index 000000000..985735fa4 --- /dev/null +++ b/sources/shiboken2/generator/_config.py.in @@ -0,0 +1,9 @@ +version = "@FINAL_PACKAGE_VERSION@" +version_info = (@shiboken_MAJOR_VERSION@, @shiboken_MINOR_VERSION@, @shiboken_MICRO_VERSION@, "@shiboken_PRE_RELEASE_VERSION_TYPE@", "@shiboken_PRE_RELEASE_VERSION@") + +@PACKAGE_BUILD_DATE@ +@PACKAGE_BUILD_COMMIT_DATE@ +@PACKAGE_BUILD_COMMIT_HASH@ +@PACKAGE_BUILD_COMMIT_HASH_DESCRIBED@ +@PACKAGE_SETUP_PY_PACKAGE_TIMESTAMP_ASSIGNMENT@ +@PACKAGE_SETUP_PY_PACKAGE_VERSION_ASSIGNMENT@ diff --git a/sources/shiboken2/generator/generator.cpp b/sources/shiboken2/generator/generator.cpp index 1e2f03932..ec227bd83 100644 --- a/sources/shiboken2/generator/generator.cpp +++ b/sources/shiboken2/generator/generator.cpp @@ -28,6 +28,7 @@ #include "generator.h" #include "abstractmetalang.h" +#include "messages.h" #include "reporthandler.h" #include "fileout.h" #include "apiextractor.h" @@ -40,8 +41,118 @@ #include <QDebug> #include <typedatabase.h> -struct Generator::GeneratorPrivate { - const ApiExtractor* apiextractor; +/** + * DefaultValue is used for storing default values of types for which code is + * generated in different contexts: + * + * Context | Example: "Class *" | Example: "Class" with default Constructor + * --------------------+-------------------------------+------------------------------------------ + * Variable | var{nullptr}; | var; + * initializations | | + * --------------------+-------------------------------+------------------------------------------ + * Return values | return nullptr; | return {} + * --------------------+-------------------------------+------------------------------------------ + * constructor | static_cast<Class *>(nullptr) | Class() + * arguments lists | | + * (recursive, precise | | + * matching). | | + */ + +DefaultValue::DefaultValue(Type t, QString value) : + m_type(t), m_value(std::move(value)) +{ +} + +DefaultValue::DefaultValue(QString customValue) : + m_type(Custom), m_value(std::move(customValue)) +{ +} + +QString DefaultValue::returnValue() const +{ + switch (m_type) { + case DefaultValue::Error: + return QLatin1String("#error"); + case DefaultValue::Boolean: + return QLatin1String("false"); + case DefaultValue::CppScalar: + return QLatin1String("0"); + case DefaultValue::Custom: + case DefaultValue::Enum: + return m_value; + case DefaultValue::Pointer: + return QLatin1String("nullptr"); + case DefaultValue::Void: + return QString(); + case DefaultValue::DefaultConstructorWithDefaultValues: + return m_value + QLatin1String("()"); + case DefaultValue::DefaultConstructor: + break; + } + return QLatin1String("{}"); +} + +QString DefaultValue::initialization() const +{ + switch (m_type) { + case DefaultValue::Error: + return QLatin1String("#error"); + case DefaultValue::Boolean: + return QLatin1String("{false}"); + case DefaultValue::CppScalar: + return QLatin1String("{0}"); + case DefaultValue::Custom: + return QLatin1String(" = ") + m_value; + case DefaultValue::Enum: + return QLatin1Char('{') + m_value + QLatin1Char('}'); + case DefaultValue::Pointer: + return QLatin1String("{nullptr}"); + case DefaultValue::Void: + Q_ASSERT(false); + break; + case DefaultValue::DefaultConstructor: + case DefaultValue::DefaultConstructorWithDefaultValues: + break; + } + return QString(); +} + +QString DefaultValue::constructorParameter() const +{ + switch (m_type) { + case DefaultValue::Error: + return QLatin1String("#error"); + case DefaultValue::Boolean: + return QLatin1String("false"); + case DefaultValue::CppScalar: { + // PYSIDE-846: Use static_cast in case of "unsigned long" and similar + const QString cast = m_value.contains(QLatin1Char(' ')) + ? QLatin1String("static_cast<") + m_value + QLatin1Char('>') + : m_value; + return cast + QLatin1String("(0)"); + } + case DefaultValue::Custom: + case DefaultValue::Enum: + return m_value; + case DefaultValue::Pointer: + // Be precise here to be able to differentiate between constructors + // taking different pointer types, cf + // QTreeWidgetItemIterator(QTreeWidget *) and + // QTreeWidgetItemIterator(QTreeWidgetItemIterator *). + return QLatin1String("static_cast<") + m_value + QLatin1String("*>(nullptr)"); + case DefaultValue::Void: + Q_ASSERT(false); + break; + case DefaultValue::DefaultConstructor: + case DefaultValue::DefaultConstructorWithDefaultValues: + break; + } + return m_value + QLatin1String("()"); +} + +struct Generator::GeneratorPrivate +{ + const ApiExtractor* apiextractor = nullptr; QString outDir; // License comment QString licenseComment; @@ -62,20 +173,17 @@ Generator::~Generator() delete m_d; } -bool Generator::setup(const ApiExtractor& extractor, const QMap< QString, QString > args) +bool Generator::setup(const ApiExtractor& extractor) { m_d->apiextractor = &extractor; - TypeEntryHash allEntries = TypeDatabase::instance()->allEntries(); + const auto &allEntries = TypeDatabase::instance()->entries(); TypeEntry* entryFound = 0; - for (TypeEntryHash::const_iterator it = allEntries.cbegin(), end = allEntries.cend(); it != end; ++it) { - for (TypeEntry *entry : it.value()) { - if (entry->type() == TypeEntry::TypeSystemType && entry->generateCode()) { - entryFound = entry; - break; - } - } - if (entryFound) + for (auto it = allEntries.cbegin(), end = allEntries.cend(); it != end; ++it) { + TypeEntry *entry = it.value(); + if (entry->type() == TypeEntry::TypeSystemType && entry->generateCode()) { + entryFound = entry; break; + } } if (entryFound) m_d->packageName = entryFound->name(); @@ -84,7 +192,7 @@ bool Generator::setup(const ApiExtractor& extractor, const QMap< QString, QStrin collectInstantiatedContainersAndSmartPointers(); - return doSetup(args); + return doSetup(); } QString Generator::getSimplifiedContainerTypeName(const AbstractMetaType* type) @@ -197,6 +305,11 @@ Generator::OptionDescriptions Generator::options() const return OptionDescriptions(); } +bool Generator::handleOption(const QString & /* key */, const QString & /* value */) +{ + return false; +} + AbstractMetaClassList Generator::classes() const { return m_d->apiextractor->classes(); @@ -227,24 +340,14 @@ ContainerTypeEntryList Generator::containerTypes() const return m_d->apiextractor->containerTypes(); } -const AbstractMetaEnum* Generator::findAbstractMetaEnum(const EnumTypeEntry* typeEntry) const -{ - return m_d->apiextractor->findAbstractMetaEnum(typeEntry); -} - const AbstractMetaEnum* Generator::findAbstractMetaEnum(const TypeEntry* typeEntry) const { return m_d->apiextractor->findAbstractMetaEnum(typeEntry); } -const AbstractMetaEnum* Generator::findAbstractMetaEnum(const FlagsTypeEntry* typeEntry) const -{ - return m_d->apiextractor->findAbstractMetaEnum(typeEntry); -} - const AbstractMetaEnum* Generator::findAbstractMetaEnum(const AbstractMetaType* metaType) const { - return m_d->apiextractor->findAbstractMetaEnum(metaType); + return m_d->apiextractor->findAbstractMetaEnum(metaType->typeEntry()); } QString Generator::licenseComment() const @@ -278,21 +381,6 @@ void Generator::setOutputDirectory(const QString &outDir) m_d->outDir = outDir; } -inline void touchFile(const QString &filePath) -{ - QFile toucher(filePath); - qint64 size = toucher.size(); - if (!toucher.open(QIODevice::ReadWrite)) { - qCWarning(lcShiboken).noquote().nospace() - << QStringLiteral("Failed to touch file '%1'") - .arg(QDir::toNativeSeparators(filePath)); - return; - } - toucher.resize(size+1); - toucher.resize(size); - toucher.close(); -} - bool Generator::generateFileForContext(GeneratorContext &context) { AbstractMetaClass *cls = context.metaClass(); @@ -312,20 +400,7 @@ bool Generator::generateFileForContext(GeneratorContext &context) generateClass(fileOut.stream, context); - FileOut::State state = fileOut.done(); - switch (state) { - case FileOut::Failure: - return false; - case FileOut::Unchanged: - // Even if contents is unchanged, the last file modification time should be updated, - // so that the build system can rely on the fact the generated file is up-to-date. - touchFile(filePath); - break; - case FileOut::Success: - break; - } - - return true; + return fileOut.done() != FileOut::Failure; } QString Generator::getFileNameBaseForSmartPointer(const AbstractMetaType *smartPointerType, @@ -369,9 +444,9 @@ bool Generator::shouldGenerate(const AbstractMetaClass* metaClass) const return shouldGenerateTypeEntry(metaClass->typeEntry()); } -void verifyDirectoryFor(const QFile &file) +void verifyDirectoryFor(const QString &file) { - QDir dir = QFileInfo(file).dir(); + QDir dir = QFileInfo(file).absoluteDir(); if (!dir.exists()) { if (!dir.mkpath(dir.absolutePath())) { qCWarning(lcShiboken).noquote().nospace() @@ -463,7 +538,7 @@ AbstractMetaFunctionList Generator::implicitConversions(const AbstractMetaType* bool Generator::isObjectType(const TypeEntry* type) { if (type->isComplex()) - return Generator::isObjectType((const ComplexTypeEntry*)type); + return Generator::isObjectType(static_cast<const ComplexTypeEntry *>(type)); return type->isObject(); } bool Generator::isObjectType(const ComplexTypeEntry* type) @@ -557,184 +632,178 @@ QString Generator::getFullTypeNameWithoutModifiers(const AbstractMetaType* type) return QLatin1String("::") + typeName; } -QString Generator::minimalConstructor(const AbstractMetaType* type) const +DefaultValue Generator::minimalConstructor(const AbstractMetaType* type) const { if (!type || (type->referenceType() == LValueReference && Generator::isObjectType(type))) - return QString(); + return DefaultValue(DefaultValue::Error); if (type->isContainer()) { QString ctor = type->cppSignature(); - if (ctor.endsWith(QLatin1Char('*'))) - return QLatin1String("0"); + if (ctor.endsWith(QLatin1Char('*'))) { + ctor.chop(1); + return DefaultValue(DefaultValue::Pointer, ctor.trimmed()); + } if (ctor.startsWith(QLatin1String("const "))) ctor.remove(0, sizeof("const ") / sizeof(char) - 1); if (ctor.endsWith(QLatin1Char('&'))) { ctor.chop(1); ctor = ctor.trimmed(); } - return QLatin1String("::") + ctor + QLatin1String("()"); + return DefaultValue(DefaultValue::DefaultConstructor, QLatin1String("::") + ctor); } if (type->isNativePointer()) - return QLatin1String("static_cast<") + type->typeEntry()->qualifiedCppName() + QLatin1String(" *>(0)"); + return DefaultValue(DefaultValue::Pointer, type->typeEntry()->qualifiedCppName()); if (Generator::isPointer(type)) - return QLatin1String("static_cast< ::") + type->typeEntry()->qualifiedCppName() + QLatin1String(" *>(0)"); + return DefaultValue(DefaultValue::Pointer, QLatin1String("::") + type->typeEntry()->qualifiedCppName()); if (type->typeEntry()->isComplex()) { - const ComplexTypeEntry* cType = reinterpret_cast<const ComplexTypeEntry*>(type->typeEntry()); - QString ctor = cType->defaultConstructor(); - if (!ctor.isEmpty()) - return ctor; - ctor = minimalConstructor(AbstractMetaClass::findClass(classes(), cType)); - if (type->hasInstantiations()) - ctor = ctor.replace(getFullTypeName(cType), getFullTypeNameWithoutModifiers(type)); + const ComplexTypeEntry* cType = static_cast<const ComplexTypeEntry*>(type->typeEntry()); + if (cType->hasDefaultConstructor()) + return DefaultValue(DefaultValue::Custom, cType->defaultConstructor()); + auto ctor = minimalConstructor(AbstractMetaClass::findClass(classes(), cType)); + if (ctor.isValid() && type->hasInstantiations()) { + QString v = ctor.value(); + v.replace(getFullTypeName(cType), getFullTypeNameWithoutModifiers(type)); + ctor.setValue(v); + } return ctor; } return minimalConstructor(type->typeEntry()); } -QString Generator::minimalConstructor(const TypeEntry* type) const +DefaultValue Generator::minimalConstructor(const TypeEntry* type) const { if (!type) - return QString(); + return DefaultValue(DefaultValue::Error); if (type->isCppPrimitive()) { const QString &name = type->qualifiedCppName(); return name == QLatin1String("bool") - ? QLatin1String("false") : name + QLatin1String("(0)"); + ? DefaultValue(DefaultValue::Boolean) + : DefaultValue(DefaultValue::CppScalar, name); } - if (type->isEnum()) - return QLatin1String("static_cast< ::") + type->qualifiedCppName() + QLatin1String(">(0)"); + if (type->isEnum()) { + const auto enumEntry = static_cast<const EnumTypeEntry *>(type); + if (const auto *nullValue = enumEntry->nullValue()) + return DefaultValue(DefaultValue::Enum, nullValue->name()); + return DefaultValue(DefaultValue::Custom, + QLatin1String("static_cast< ::") + type->qualifiedCppName() + + QLatin1String(">(0)")); + } - if (type->isFlags()) - return type->qualifiedCppName() + QLatin1String("(0)"); + if (type->isFlags()) { + return DefaultValue(DefaultValue::Custom, + type->qualifiedCppName() + QLatin1String("(0)")); + } if (type->isPrimitive()) { - QString ctor = reinterpret_cast<const PrimitiveTypeEntry*>(type)->defaultConstructor(); + QString ctor = static_cast<const PrimitiveTypeEntry*>(type)->defaultConstructor(); // If a non-C++ (i.e. defined by the user) primitive type does not have // a default constructor defined by the user, the empty constructor is // heuristically returned. If this is wrong the build of the generated // bindings will tell. return ctor.isEmpty() - ? (QLatin1String("::") + type->qualifiedCppName() + QLatin1String("()")) - : ctor; + ? DefaultValue(DefaultValue::DefaultConstructorWithDefaultValues, QLatin1String("::") + + type->qualifiedCppName()) + : DefaultValue(DefaultValue::Custom, ctor); } if (type->isComplex()) return minimalConstructor(AbstractMetaClass::findClass(classes(), type)); - return QString(); + return DefaultValue(DefaultValue::Error); } -QString Generator::minimalConstructor(const AbstractMetaClass* metaClass) const +static QString constructorCall(const QString &qualifiedCppName, const QStringList &args) +{ + return QLatin1String("::") + qualifiedCppName + QLatin1Char('(') + + args.join(QLatin1String(", ")) + QLatin1Char(')'); +} + +DefaultValue Generator::minimalConstructor(const AbstractMetaClass* metaClass) const { if (!metaClass) - return QString(); + return DefaultValue(DefaultValue::Error); - const ComplexTypeEntry* cType = reinterpret_cast<const ComplexTypeEntry*>(metaClass->typeEntry()); + const ComplexTypeEntry* cType = static_cast<const ComplexTypeEntry*>(metaClass->typeEntry()); if (cType->hasDefaultConstructor()) - return cType->defaultConstructor(); + return DefaultValue(DefaultValue::Custom, cType->defaultConstructor()); + const QString qualifiedCppName = cType->qualifiedCppName(); + // Obtain a list of constructors sorted by complexity and number of arguments + QMultiMap<int, const AbstractMetaFunction *> candidates; const AbstractMetaFunctionList &constructors = metaClass->queryFunctions(AbstractMetaClass::Constructors); - int maxArgs = 0; for (const AbstractMetaFunction *ctor : constructors) { - if (ctor->isUserAdded() || ctor->isPrivate() || ctor->functionType() != AbstractMetaFunction::ConstructorFunction) - continue; - - int numArgs = ctor->arguments().size(); - if (numArgs == 0) { - maxArgs = 0; - break; - } - if (numArgs > maxArgs) - maxArgs = numArgs; - } - - QString qualifiedCppName = metaClass->typeEntry()->qualifiedCppName(); - QStringList templateTypes; - const QVector<TypeEntry *> &templateArguments = metaClass->templateArguments(); - for (TypeEntry *templateType : templateArguments) - templateTypes << templateType->qualifiedCppName(); - - // Empty constructor. - if (maxArgs == 0) - return QLatin1String("::") + qualifiedCppName + QLatin1String("()"); - - QVector<const AbstractMetaFunction *> candidates; - - // Constructors with C++ primitive types, enums or pointers only. - // Start with the ones with fewer arguments. - for (int i = 1; i <= maxArgs; ++i) { - for (const AbstractMetaFunction *ctor : constructors) { - if (ctor->isUserAdded() || ctor->isPrivate() || ctor->functionType() != AbstractMetaFunction::ConstructorFunction) - continue; - - const AbstractMetaArgumentList &arguments = ctor->arguments(); - if (arguments.size() != i) - continue; - - QStringList args; - for (const AbstractMetaArgument *arg : arguments) { - const TypeEntry* type = arg->type()->typeEntry(); - if (type == metaClass->typeEntry()) { - args.clear(); - break; - } - - if (!arg->originalDefaultValueExpression().isEmpty()) { - if (!arg->defaultValueExpression().isEmpty() - && arg->defaultValueExpression() != arg->originalDefaultValueExpression()) { - args << arg->defaultValueExpression(); - } - break; - } - - if (type->isCppPrimitive() || type->isEnum() || isPointer(arg->type())) { - QString argValue = minimalConstructor(arg->type()); - if (argValue.isEmpty()) { - args.clear(); - break; - } - args << argValue; - } else { - args.clear(); - break; - } + if (!ctor->isUserAdded() && !ctor->isPrivate() + && ctor->functionType() == AbstractMetaFunction::ConstructorFunction) { + // No arguments: Default constructible + const auto &arguments = ctor->arguments(); + if (arguments.isEmpty()) { + return DefaultValue(DefaultValue::DefaultConstructor, + QLatin1String("::") + qualifiedCppName); } - - if (!args.isEmpty()) - return QString::fromLatin1("::%1(%2)").arg(qualifiedCppName, args.join(QLatin1String(", "))); - - candidates << ctor; + // First argument has unmodified default: Default constructible with values + if (arguments.constFirst()->hasUnmodifiedDefaultValueExpression()) { + return DefaultValue(DefaultValue::DefaultConstructorWithDefaultValues, + QLatin1String("::") + qualifiedCppName); + } + // Examine arguments, exclude functions taking a self parameter + bool simple = true; + bool suitable = true; + for (int i = 0, size = arguments.size(); + suitable && i < size && !arguments.at(i)->hasDefaultValueExpression(); ++i) { + const AbstractMetaArgument *arg = arguments.at(i); + const TypeEntry *aType = arg->type()->typeEntry(); + suitable &= aType != cType; + simple &= aType->isCppPrimitive() || aType->isEnum() || isPointer(arg->type()); + } + if (suitable) + candidates.insert(arguments.size() + (simple ? 0 : 100), ctor); } } - // Constructors with C++ primitive types, enums, pointers, value types, - // and user defined primitive types. - // Builds the minimal constructor recursively. - for (const AbstractMetaFunction *ctor : qAsConst(candidates)) { + for (auto it = candidates.cbegin(), end = candidates.cend(); it != end; ++it) { + const AbstractMetaArgumentList &arguments = it.value()->arguments(); QStringList args; - const AbstractMetaArgumentList &arguments = ctor->arguments(); - for (const AbstractMetaArgument *arg : arguments) { - if (arg->type()->typeEntry() == metaClass->typeEntry()) { - args.clear(); - break; - } - QString argValue = minimalConstructor(arg->type()); - if (argValue.isEmpty()) { - args.clear(); + bool ok = true; + for (int i =0, size = arguments.size(); ok && i < size; ++i) { + const AbstractMetaArgument *arg = arguments.at(i); + if (arg->hasDefaultValueExpression()) { + if (arg->hasModifiedDefaultValueExpression()) + args << arg->defaultValueExpression(); // Spell out modified values break; } - args << argValue; - } - if (!args.isEmpty()) { - return QString::fromLatin1("::%1(%2)").arg(qualifiedCppName, args.join(QLatin1String(", "))); + auto argValue = minimalConstructor(arg->type()); + ok &= argValue.isValid(); + args << argValue.constructorParameter(); } + if (ok) + return DefaultValue(DefaultValue::Custom, constructorCall(qualifiedCppName, args)); } - return QString(); + return DefaultValue(DefaultValue::Error); +} + +// Should int be used for a (protected) enum when generating the public wrapper? +bool Generator::useEnumAsIntForProtectedHack(const AbstractMetaType *metaType) const +{ + if (metaType->isFlags()) + return true; + if (!metaType->isEnum()) + return false; + const AbstractMetaEnum *metaEnum = findAbstractMetaEnum(metaType); + if (!metaEnum) + return true; + if (metaEnum->attributes() & AbstractMetaAttributes::Public) // No reason, type is public + return false; + // Only ordinary C-enums can be used as int, scoped enums fail when used + // as function arguments. + if (metaEnum->enumKind() == EnumKind::EnumClass) + qWarning(lcShiboken, "%s", qPrintable(msgCannotUseEnumAsInt(metaEnum->name()))); + return true; } QString Generator::translateType(const AbstractMetaType *cType, @@ -754,7 +823,7 @@ QString Generator::translateType(const AbstractMetaType *cType, s = QLatin1String("void"); } else if (cType->isArray()) { s = translateType(cType->arrayElementType(), context, options) + QLatin1String("[]"); - } else if (options & Generator::EnumAsInts && (cType->isEnum() || cType->isFlags())) { + } else if ((options & Generator::EnumAsInts) && useEnumAsIntForProtectedHack(cType)) { s = QLatin1String("int"); } else { if (options & Generator::OriginalName) { diff --git a/sources/shiboken2/generator/generator.h b/sources/shiboken2/generator/generator.h index 010ed868c..225e7aec7 100644 --- a/sources/shiboken2/generator/generator.h +++ b/sources/shiboken2/generator/generator.h @@ -57,7 +57,7 @@ class ContainerTypeEntry; class Indentor; QTextStream& formatCode(QTextStream &s, const QString& code, Indentor &indentor); -void verifyDirectoryFor(const QFile &file); +void verifyDirectoryFor(const QString &file); QString getClassTargetFullName(const AbstractMetaClass* metaClass, bool includePackageName = true); QString getClassTargetFullName(const AbstractMetaEnum* metaEnum, bool includePackageName = true); @@ -95,6 +95,42 @@ const int alwaysGenerateDestructor = 1; const int alwaysGenerateDestructor = 0; #endif +class DefaultValue +{ +public: + enum Type + { + Error, + Boolean, + CppScalar, // A C++ scalar type (int,..) specified by value() + Custom, // A custom constructor/expression, uses value() as is + DefaultConstructor, // For classes named value() + DefaultConstructorWithDefaultValues, // as DefaultConstructor, but can't return {} though. + Enum, // Enum value as specified by value() + Pointer, // Pointer of type value() + Void // "", for return values only + }; + + explicit DefaultValue(Type t = Error, QString value = QString()); + explicit DefaultValue(QString customValue); + + bool isValid() const { return m_type != Error; } + + QString returnValue() const; + QString initialization() const; + QString constructorParameter() const; + + QString value() const { return m_value; } + void setValue(const QString &value) { m_value = value; } + + Type type() const { return m_type; } + void setType(Type type) { m_type = type; } + +private: + Type m_type; + QString m_value; +}; + /** * A GeneratorContext object contains a pointer to an AbstractMetaClass and/or a specialized * AbstractMetaType, for which code is currently being generated. @@ -166,13 +202,74 @@ public: Generator(); virtual ~Generator(); - bool setup(const ApiExtractor& extractor, const QMap<QString, QString> args); + bool setup(const ApiExtractor& extractor); virtual OptionDescriptions options() const; + virtual bool handleOption(const QString &key, const QString &value); /// Returns the classes used to generate the binding code. AbstractMetaClassList classes() const; + /// Returns the output directory + QString outputDirectory() const; + + /// Set the output directory + void setOutputDirectory(const QString &outDir); + + /** + * Start the code generation, be sure to call setClasses before callign this method. + * For each class it creates a QTextStream, call the write method with the current + * class and the associated text stream, then write the text stream contents if needed. + * \see #write + */ + bool generate(); + + /// Returns the license comment to be prepended to each source file generated. + QString licenseComment() const; + + /// Sets the license comment to be prepended to each source file generated. + void setLicenseComment(const QString &licenseComment); + + /// Returns the generator's name. Used for cosmetic purposes. + virtual const char* name() const = 0; + + /** + * Retrieves the name of the currently processed module. + * While package name is a complete package idetification, e.g. 'PySide.QtCore', + * a module name represents the last part of the package, e.g. 'QtCore'. + * If the target language separates the modules with characters other than + * dots ('.') the generator subclass must overload this method. + * \return a string representing the last part of a package name + */ + QString moduleName() const; + + /** + * Retrieves a list of constructors used in implicit conversions + * available on the given type. The TypeEntry must be a value-type + * or else it will return an empty list. + * \param type a TypeEntry that is expected to be a value-type + * \return a list of constructors that could be used as implicit converters + */ + AbstractMetaFunctionList implicitConversions(const TypeEntry* type) const; + + /// Convenience function for implicitConversions(const TypeEntry* type). + AbstractMetaFunctionList implicitConversions(const AbstractMetaType* metaType) const; + + /// Check if type is a pointer. + static bool isPointer(const AbstractMetaType* type); + + /// Tells if the type or class is an Object (or QObject) Type. + static bool isObjectType(const TypeEntry* type); + static bool isObjectType(const ComplexTypeEntry* type); + static bool isObjectType(const AbstractMetaType* metaType); + static bool isObjectType(const AbstractMetaClass* metaClass); + + /// Returns true if the type is a C string (const char*). + static bool isCString(const AbstractMetaType* type); + /// Returns true if the type is a void pointer. + static bool isVoidPointer(const AbstractMetaType* type); + +protected: /// Returns the classes, topologically ordered, used to generate the binding code. /// /// The classes are ordered such that derived classes appear later in the list than @@ -191,33 +288,12 @@ public: /// Returns all container types found by APIExtractor ContainerTypeEntryList containerTypes() const; - /// Returns an AbstractMetaEnum for a given EnumTypeEntry, or NULL if not found. - const AbstractMetaEnum* findAbstractMetaEnum(const EnumTypeEntry* typeEntry) const; - /// Returns an AbstractMetaEnum for a given TypeEntry that is an EnumTypeEntry, or NULL if not found. const AbstractMetaEnum* findAbstractMetaEnum(const TypeEntry* typeEntry) const; - /// Returns an AbstractMetaEnum for the enum related to a given FlagsTypeEntry, or NULL if not found. - const AbstractMetaEnum* findAbstractMetaEnum(const FlagsTypeEntry* typeEntry) const; - /// Returns an AbstractMetaEnum for a given AbstractMetaType that holds an EnumTypeEntry, or NULL if not found. const AbstractMetaEnum* findAbstractMetaEnum(const AbstractMetaType* metaType) const; - /// Returns the output directory - QString outputDirectory() const; - - /// Set the output directory - void setOutputDirectory(const QString &outDir); - - /** - * Start the code generation, be sure to call setClasses before callign this method. - * For each class it creates a QTextStream, call the write method with the current - * class and the associated text stream, then write the text stream contents if needed. - * \see #write - */ - bool generate(); - - /// Generates a file for given AbstractMetaClass or AbstractMetaType (smart pointer case). bool generateFileForContext(GeneratorContext &context); @@ -225,9 +301,6 @@ public: QString getFileNameBaseForSmartPointer(const AbstractMetaType *smartPointerType, const AbstractMetaClass *smartPointerClass) const; - /// Returns the generator's name. Used for cosmetic purposes. - virtual const char* name() const = 0; - /// Returns true if the generator should generate any code for the TypeEntry. bool shouldGenerateTypeEntry(const TypeEntry*) const; @@ -266,56 +339,10 @@ public: void replaceTemplateVariables(QString &code, const AbstractMetaFunction *func); /** - * Returns the license comment to be prepended to each source file generated. - */ - QString licenseComment() const; - - /** - * Sets the license comment to be prepended to each source file generated. - */ - void setLicenseComment(const QString &licenseComment); - - /** * Returns the package name. */ QString packageName() const; - /** - * Retrieves the name of the currently processed module. - * While package name is a complete package idetification, e.g. 'PySide.QtCore', - * a module name represents the last part of the package, e.g. 'QtCore'. - * If the target language separates the modules with characters other than - * dots ('.') the generator subclass must overload this method. - * \return a string representing the last part of a package name - */ - virtual QString moduleName() const; - - /** - * Retrieves a list of constructors used in implicit conversions - * available on the given type. The TypeEntry must be a value-type - * or else it will return an empty list. - * \param type a TypeEntry that is expected to be a value-type - * \return a list of constructors that could be used as implicit converters - */ - AbstractMetaFunctionList implicitConversions(const TypeEntry* type) const; - - /// Convenience function for implicitConversions(const TypeEntry* type). - AbstractMetaFunctionList implicitConversions(const AbstractMetaType* metaType) const; - - /// Check if type is a pointer. - static bool isPointer(const AbstractMetaType* type); - - /// Tells if the type or class is an Object (or QObject) Type. - static bool isObjectType(const TypeEntry* type); - static bool isObjectType(const ComplexTypeEntry* type); - static bool isObjectType(const AbstractMetaType* metaType); - static bool isObjectType(const AbstractMetaClass* metaClass); - - /// Returns true if the type is a C string (const char*). - static bool isCString(const AbstractMetaType* type); - /// Returns true if the type is a void pointer. - static bool isVoidPointer(const AbstractMetaType* type); - // Returns the full name of the type. QString getFullTypeName(const TypeEntry* type) const; QString getFullTypeName(const AbstractMetaType* type) const; @@ -333,11 +360,10 @@ public: * It will check first for a user defined default constructor. * Returns a null string if it fails. */ - QString minimalConstructor(const TypeEntry* type) const; - QString minimalConstructor(const AbstractMetaType* type) const; - QString minimalConstructor(const AbstractMetaClass* metaClass) const; + DefaultValue minimalConstructor(const TypeEntry* type) const; + DefaultValue minimalConstructor(const AbstractMetaType* type) const; + DefaultValue minimalConstructor(const AbstractMetaClass* metaClass) const; -protected: /** * Returns the file name used to write the binding code of an AbstractMetaClass/Type. * \param context the GeneratorContext which contains an AbstractMetaClass or AbstractMetaType @@ -348,7 +374,7 @@ protected: virtual QString fileNameForContext(GeneratorContext &context) const = 0; - virtual bool doSetup(const QMap<QString, QString>& args) = 0; + virtual bool doSetup() = 0; /** * Write the bindding code for an AbstractMetaClass. @@ -378,6 +404,8 @@ protected: const QString &context); private: + bool useEnumAsIntForProtectedHack(const AbstractMetaType *cType) const; + struct GeneratorPrivate; GeneratorPrivate* m_d; void collectInstantiatedContainersAndSmartPointers(const AbstractMetaFunction* func); diff --git a/sources/shiboken2/generator/main.cpp b/sources/shiboken2/generator/main.cpp index 0b7c15244..362191fd0 100644 --- a/sources/shiboken2/generator/main.cpp +++ b/sources/shiboken2/generator/main.cpp @@ -34,6 +34,9 @@ #include <QtCore/QDir> #include <iostream> #include <apiextractor.h> +#include <fileout.h> +#include <typedatabase.h> +#include <messages.h> #include "generator.h" #include "shibokenconfig.h" #include "cppgenerator.h" @@ -41,9 +44,9 @@ #include "qtdocgenerator.h" #ifdef _WINDOWS - #define PATH_SPLITTER ";" +static const QChar pathSplitter = QLatin1Char(';'); #else - #define PATH_SPLITTER ":" +static const QChar pathSplitter = QLatin1Char(':'); #endif static inline QString languageLevelOption() { return QStringLiteral("language-level"); } @@ -52,95 +55,12 @@ static inline QString frameworkIncludePathOption() { return QStringLiteral("fram static inline QString systemIncludePathOption() { return QStringLiteral("system-include-paths"); } static inline QString typesystemPathOption() { return QStringLiteral("typesystem-paths"); } static inline QString helpOption() { return QStringLiteral("help"); } -static const char helpHint[] = "Note: use --help or -h for more information.\n"; - -namespace { - -class ArgsHandler -{ -public: - explicit ArgsHandler(const QMap<QString, QString>& other); - virtual ~ArgsHandler(); - - inline QMap<QString, QString>& args() const - { - return *m_args; - } - - inline bool argExists(const QString& s) const - { - return m_args->contains(s); - } - - QString removeArg(const QString& s); - bool argExistsRemove(const QString& s); +static inline QString diffOption() { return QStringLiteral("diff"); } +static inline QString dryrunOption() { return QStringLiteral("dry-run"); } - inline QString argValue(const QString& s) const - { - return m_args->value(s); - } - - inline bool noArgs() const - { - return m_args->isEmpty(); - } - - QString errorMessage() const; - -private: - QMap<QString, QString>* m_args; -}; - -ArgsHandler::ArgsHandler(const QMap<QString, QString>& other) - : m_args(new QMap<QString, QString>(other)) -{ -} - -ArgsHandler::~ArgsHandler() -{ - delete m_args; -} - -QString ArgsHandler::removeArg(const QString& s) -{ - QString retval; - - if (argExists(s)) { - retval = argValue(s); - m_args->remove(s); - } - - return retval; -} - -bool ArgsHandler::argExistsRemove(const QString& s) -{ - bool retval = false; - - if (argExists(s)) { - retval = true; - m_args->remove(s); - } +static const char helpHint[] = "Note: use --help or -h for more information.\n"; - return retval; -} - -QString ArgsHandler::errorMessage() const -{ - typedef QMap<QString, QString>::ConstIterator StringMapConstIt; - - QString message; - QTextStream str(&message); - str << "shiboken: Called with wrong arguments:"; - for (StringMapConstIt it = m_args->cbegin(), end = m_args->cend(); it != end; ++it) { - str << ' ' << it.key(); - if (!it.value().isEmpty()) - str << ' ' << it.value(); - } - str << "\nCommand line: " << QCoreApplication::arguments().join(QLatin1Char(' ')); - return message; -} -} +typedef QMap<QString, QString> CommandArgumentMap; typedef Generator::OptionDescriptions OptionDescriptions; @@ -214,18 +134,18 @@ static bool processProjectFile(QFile& projectFile, QMap<QString, QString>& args) } if (!includePaths.isEmpty()) - args.insert(includePathOption(), includePaths.join(QLatin1String(PATH_SPLITTER))); + args.insert(includePathOption(), includePaths.join(pathSplitter)); if (!frameworkIncludePaths.isEmpty()) args.insert(frameworkIncludePathOption(), - frameworkIncludePaths.join(QLatin1String(PATH_SPLITTER))); + frameworkIncludePaths.join(pathSplitter)); if (!systemIncludePaths.isEmpty()) { args.insert(systemIncludePathOption(), - systemIncludePaths.join(QLatin1String(PATH_SPLITTER))); + systemIncludePaths.join(pathSplitter)); } if (!typesystemPaths.isEmpty()) - args.insert(typesystemPathOption(), typesystemPaths.join(QLatin1String(PATH_SPLITTER))); + args.insert(typesystemPathOption(), typesystemPaths.join(pathSplitter)); if (!apiVersions.isEmpty()) args.insert(QLatin1String("api-version"), apiVersions.join(QLatin1Char('|'))); if (!languageLevel.isEmpty()) @@ -233,9 +153,9 @@ static bool processProjectFile(QFile& projectFile, QMap<QString, QString>& args) return true; } -static QMap<QString, QString> getInitializedArguments() +static CommandArgumentMap getInitializedArguments() { - QMap<QString, QString> args; + CommandArgumentMap args; QStringList arguments = QCoreApplication::arguments(); QString appName = arguments.constFirst(); arguments.removeFirst(); @@ -277,11 +197,11 @@ static QMap<QString, QString> getInitializedArguments() // Concatenate values of path arguments that can occur multiple times on the // command line. static void addPathOptionValue(const QString &option, const QString &value, - QMap<QString, QString> &args) + CommandArgumentMap &args) { - const QMap<QString, QString>::iterator it = args.find(option); + const CommandArgumentMap::iterator it = args.find(option); if (it != args.end()) - it.value().append(QLatin1String(PATH_SPLITTER) + value); + it.value().append(pathSplitter + value); else args.insert(option, value); } @@ -369,7 +289,9 @@ void printUsage() s << "Usage:\n " << "shiboken [options] header-file typesystem-file\n\n" << "General options:\n"; - const QString pathSyntax = QLatin1String("<path>[" PATH_SPLITTER "<path>" PATH_SPLITTER "...]"); + QString pathSyntax; + QTextStream(&pathSyntax) << "<path>[" << pathSplitter << "<path>" + << pathSplitter << "...]"; OptionDescriptions generalOptions = OptionDescriptions() << qMakePair(QLatin1String("api-version=<\"package mask\">,<\"version\">"), QLatin1String("Specify the supported api version used to generate the bindings")) @@ -380,18 +302,22 @@ void printUsage() << qMakePair(QLatin1String("drop-type-entries=\"<TypeEntry0>[;TypeEntry1;...]\""), QLatin1String("Semicolon separated list of type system entries (classes, namespaces,\n" "global functions and enums) to be dropped from generation.")) - << qMakePair(QLatin1String("-F") + pathSyntax, QString()) + << qMakePair(QLatin1String("-F<path>"), QString()) << qMakePair(QLatin1String("framework-include-paths=") + pathSyntax, QLatin1String("Framework include paths used by the C++ parser")) - << qMakePair(QLatin1String("-isystem") + pathSyntax, QString()) + << qMakePair(QLatin1String("-isystem<path>"), QString()) << qMakePair(QLatin1String("system-include-paths=") + pathSyntax, QLatin1String("System include paths used by the C++ parser")) << qMakePair(QLatin1String("generator-set=<\"generator module\">"), QLatin1String("generator-set to be used. e.g. qtdoc")) + << qMakePair(diffOption(), + QLatin1String("Print a diff of wrapper files")) + << qMakePair(dryrunOption(), + QLatin1String("Dry run, do not generate wrapper files")) << qMakePair(QLatin1String("-h"), QString()) << qMakePair(helpOption(), QLatin1String("Display this help and exit")) - << qMakePair(QLatin1String("-I") + pathSyntax, QString()) + << qMakePair(QLatin1String("-I<path>"), QString()) << qMakePair(QLatin1String("include-paths=") + pathSyntax, QLatin1String("Include paths used by the C++ parser")) << qMakePair(languageLevelOption() + QLatin1String("=, -std=<level>"), @@ -407,7 +333,7 @@ void printUsage() "Replaces and overrides command line arguments")) << qMakePair(QLatin1String("silent"), QLatin1String("Avoid printing any message")) - << qMakePair(QLatin1String("-T") + pathSyntax, QString()) + << qMakePair(QLatin1String("-T<path>"), QString()) << qMakePair(QLatin1String("typesystem-paths=") + pathSyntax, QLatin1String("Paths used when searching for typesystems")) << qMakePair(QLatin1String("version"), @@ -438,19 +364,15 @@ static inline void errorPrint(const QString& s) << "\nCommand line: " << qPrintable(arguments.join(QLatin1Char(' '))) << '\n'; } -static QString msgInvalidVersion(const QString &package, const QString &version) -{ - return QLatin1String("Invalid version \"") + version - + QLatin1String("\" specified for package ") + package + QLatin1Char('.'); -} - static void parseIncludePathOption(const QString &option, HeaderType headerType, - ArgsHandler &args, + CommandArgumentMap &args, ApiExtractor &extractor) { - const QString path = args.removeArg(option); - if (!path.isEmpty()) { - const QStringList includePathListList = path.split(QLatin1String(PATH_SPLITTER)); + const CommandArgumentMap::iterator it = args.find(option); + if (it != args.end()) { + const QStringList includePathListList = + it.value().split(pathSplitter, QString::SkipEmptyParts); + args.erase(it); for (const QString &s : includePathListList) extractor.addIncludePath(HeaderPath{QFile::encodeName(s), headerType}); } @@ -469,19 +391,24 @@ int main(int argc, char *argv[]) qCDebug(lcShiboken()).noquote().nospace() << QCoreApplication::arguments().join(QLatin1Char(' ')); // Store command arguments in a map - QMap<QString, QString> args = getCommandLineArgs(); - ArgsHandler argsHandler(args); + CommandArgumentMap args = getCommandLineArgs(); Generators generators; - if (argsHandler.argExistsRemove(QLatin1String("version"))) { + CommandArgumentMap::iterator ait = args.find(QLatin1String("version")); + if (ait != args.end()) { + args.erase(ait); printVerAndBanner(); return EXIT_SUCCESS; } - QString generatorSet = argsHandler.removeArg(QLatin1String("generator-set")); - // Also check QLatin1String("generatorSet") command line argument for backward compatibility. - if (generatorSet.isEmpty()) - generatorSet = argsHandler.removeArg(QLatin1String("generatorSet")); + QString generatorSet; + ait = args.find(QLatin1String("generator-set")); + if (ait == args.end()) // Also check QLatin1String("generatorSet") command line argument for backward compatibility. + ait = args.find(QLatin1String("generatorSet")); + if (ait != args.end()) { + generatorSet = ait.value(); + args.erase(ait); + } // Pre-defined generator sets. if (generatorSet == QLatin1String("qtdoc")) { @@ -497,28 +424,45 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - if (argsHandler.argExistsRemove(QLatin1String("help"))) { + ait = args.find(QLatin1String("help")); + if (ait != args.end()) { + args.erase(ait); printUsage(); return EXIT_SUCCESS; } + ait = args.find(diffOption()); + if (ait != args.end()) { + args.erase(ait); + FileOut::diff = true; + } + + ait = args.find(dryrunOption()); + if (ait != args.end()) { + args.erase(ait); + FileOut::dummy = true; + } + QString licenseComment; - QString licenseFileName = argsHandler.removeArg(QLatin1String("license-file")); - if (!licenseFileName.isEmpty()) { - if (QFile::exists(licenseFileName)) { - QFile licenseFile(licenseFileName); - if (licenseFile.open(QIODevice::ReadOnly)) - licenseComment = QString::fromUtf8(licenseFile.readAll()); + ait = args.find(QLatin1String("license-file")); + if (ait != args.end()) { + QFile licenseFile(ait.value()); + args.erase(ait); + if (licenseFile.open(QIODevice::ReadOnly)) { + licenseComment = QString::fromUtf8(licenseFile.readAll()); } else { - errorPrint(QStringLiteral("Couldn't find the file containing the license heading: %1"). - arg(licenseFileName)); + errorPrint(QStringLiteral("Could not open the file \"%1\" containing the license heading: %2"). + arg(QDir::toNativeSeparators(licenseFile.fileName()), licenseFile.errorString())); return EXIT_FAILURE; } } - QString outputDirectory = argsHandler.removeArg(QLatin1String("output-directory")); - if (outputDirectory.isEmpty()) - outputDirectory = QLatin1String("out"); + QString outputDirectory = QLatin1String("out"); + ait = args.find(QLatin1String("output-directory")); + if (ait != args.end()) { + outputDirectory = ait.value(); + args.erase(ait); + } if (!QDir(outputDirectory).exists()) { if (!QDir().mkpath(outputDirectory)) { @@ -532,11 +476,15 @@ int main(int argc, char *argv[]) ApiExtractor extractor; extractor.setLogDirectory(outputDirectory); - if (argsHandler.argExistsRemove(QLatin1String("silent"))) { + ait = args.find(QLatin1String("silent")); + if (ait != args.end()) { extractor.setSilent(true); + args.erase(ait); } else { - QString level = argsHandler.removeArg(QLatin1String("debug-level")); - if (!level.isEmpty()) { + ait = args.find(QLatin1String("debug-level")); + if (ait != args.end()) { + const QString level = ait.value(); + args.erase(ait); if (level == QLatin1String("sparse")) extractor.setDebugLevel(ReportHandler::SparseDebug); else if (level == QLatin1String("medium")) @@ -545,11 +493,15 @@ int main(int argc, char *argv[]) extractor.setDebugLevel(ReportHandler::FullDebug); } } - if (argsHandler.argExistsRemove(QLatin1String("no-suppress-warnings"))) + ait = args.find(QLatin1String("no-suppress-warnings")); + if (ait != args.end()) { + args.erase(ait); extractor.setSuppressWarnings(false); - - if (argsHandler.argExists(QLatin1String("api-version"))) { - const QStringList &versions = argsHandler.removeArg(QLatin1String("api-version")).split(QLatin1Char('|')); + } + ait = args.find(QLatin1String("api-version")); + if (ait != args.end()) { + const QStringList &versions = ait.value().split(QLatin1Char('|')); + args.erase(ait); for (const QString &fullVersion : versions) { QStringList parts = fullVersion.split(QLatin1Char(',')); QString package; @@ -563,54 +515,65 @@ int main(int argc, char *argv[]) } } - if (argsHandler.argExists(QLatin1String("drop-type-entries"))) - extractor.setDropTypeEntries(argsHandler.removeArg(QLatin1String("drop-type-entries"))); + ait = args.find(QLatin1String("drop-type-entries")); + if (ait != args.end()) { + extractor.setDropTypeEntries(ait.value()); + args.erase(ait); + } - QString path = argsHandler.removeArg(QLatin1String("typesystem-paths")); - if (!path.isEmpty()) - extractor.addTypesystemSearchPath(path.split(QLatin1String(PATH_SPLITTER))); + ait = args.find(QLatin1String("typesystem-paths")); + if (ait != args.end()) { + extractor.addTypesystemSearchPath(ait.value().split(pathSplitter)); + args.erase(ait); + } parseIncludePathOption(includePathOption(), HeaderType::Standard, - argsHandler, extractor); + args, extractor); parseIncludePathOption(frameworkIncludePathOption(), HeaderType::Framework, - argsHandler, extractor); + args, extractor); parseIncludePathOption(systemIncludePathOption(), HeaderType::System, - argsHandler, extractor); + args, extractor); - QString cppFileName = argsHandler.removeArg(QLatin1String("arg-1")); + ait = args.find(QLatin1String("arg-1")); + if (ait == args.end()) { + errorPrint(QLatin1String("Required argument header-file is missing.")); + return EXIT_FAILURE; + } + const QString cppFileName = ait.value(); + args.erase(ait); const QFileInfo cppFileNameFi(cppFileName); if (!cppFileNameFi.isFile() && !cppFileNameFi.isSymLink()) { errorPrint(QLatin1Char('"') + cppFileName + QLatin1String("\" does not exist.")); return EXIT_FAILURE; } - QString typeSystemFileName = argsHandler.removeArg(QLatin1String("arg-2")); + ait = args.find(QLatin1String("arg-2")); + if (ait == args.end()) { + errorPrint(QLatin1String("Required argument typesystem-file is missing.")); + return EXIT_FAILURE; + } + const QString typeSystemFileName = ait.value(); + args.erase(ait); QString messagePrefix = QFileInfo(typeSystemFileName).baseName(); if (messagePrefix.startsWith(QLatin1String("typesystem_"))) messagePrefix.remove(0, 11); ReportHandler::setPrefix(QLatin1Char('(') + messagePrefix + QLatin1Char(')')); - /* Make sure to remove the project file's arguments (if any) and - * --project-file, also the arguments of each generator before - * checking if there isn't any existing arguments in argsHandler. - */ - argsHandler.removeArg(QLatin1String("project-file")); - QMap<QString, QString> projectFileArgs = getInitializedArguments(); - if (!projectFileArgs.isEmpty()) { - QMap<QString, QString>::const_iterator it = - projectFileArgs.constBegin(); - for ( ; it != projectFileArgs.constEnd(); ++it) - argsHandler.removeArg(it.key()); - } - for (const GeneratorPtr &generator : qAsConst(generators)) { - const OptionDescriptions &options = generator->options(); - for (const auto &od : options) - argsHandler.removeArg(od.first); - } - - const QString languageLevel = argsHandler.removeArg(languageLevelOption()); - if (!languageLevel.isEmpty()) { - const QByteArray languageLevelBA = languageLevel.toLatin1(); + // Pass option to all generators (Cpp/Header generator have the same options) + for (ait = args.begin(); ait != args.end(); ) { + bool found = false; + for (const GeneratorPtr &generator : qAsConst(generators)) + found |= generator->handleOption(ait.key(), ait.value()); + if (found) + ait = args.erase(ait); + else + ++ait; + } + + ait = args.find(languageLevelOption()); + if (ait != args.end()) { + const QByteArray languageLevelBA = ait.value().toLatin1(); + args.erase(ait); const LanguageLevel level = clang::languageLevelFromOption(languageLevelBA.constData()); if (level == LanguageLevel::Default) { std::cout << "Invalid argument for language level: \"" @@ -620,8 +583,17 @@ int main(int argc, char *argv[]) extractor.setLanguageLevel(level); } - if (!argsHandler.noArgs()) { - errorPrint(argsHandler.errorMessage()); + /* Make sure to remove the project file's arguments (if any) and + * --project-file, also the arguments of each generator before + * checking if there isn't any existing arguments in argsHandler. + */ + args.remove(QLatin1String("project-file")); + CommandArgumentMap projectFileArgs = getInitializedArguments(); + for (auto it = projectFileArgs.cbegin(), end = projectFileArgs.cend(); it != end; ++it) + args.remove(it.key()); + + if (!args.isEmpty()) { + errorPrint(msgLeftOverArguments(args)); std::cout << helpHint; return EXIT_FAILURE; } @@ -642,17 +614,16 @@ int main(int argc, char *argv[]) if (!extractor.classCount()) qCWarning(lcShiboken) << "No C++ classes found!"; - qCDebug(lcShiboken) << extractor; + qCDebug(lcShiboken) << extractor << '\n' + << *TypeDatabase::instance(); for (const GeneratorPtr &g : qAsConst(generators)) { g->setOutputDirectory(outputDirectory); g->setLicenseComment(licenseComment); - if (g->setup(extractor, args)) { - if (!g->generate()) { - errorPrint(QLatin1String("Error running generator: ") - + QLatin1String(g->name()) + QLatin1Char('.')); - return EXIT_FAILURE; - } + if (!g->setup(extractor) || !g->generate()) { + errorPrint(QLatin1String("Error running generator: ") + + QLatin1String(g->name()) + QLatin1Char('.')); + return EXIT_FAILURE; } } diff --git a/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp b/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp index d47ba8bd7..19ddd5f37 100644 --- a/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp +++ b/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp @@ -28,6 +28,7 @@ #include "qtdocgenerator.h" #include <abstractmetalang.h> +#include <messages.h> #include <reporthandler.h> #include <typesystem.h> #include <qtdocparser.h> @@ -202,34 +203,6 @@ private: const QString &m_label; }; -static QString msgTagWarning(const QXmlStreamReader &reader, const QString &context, - const QString &tag, const QString &message) -{ - QString result; - QTextStream str(&result); - str << "While handling <"; - const QStringRef currentTag = reader.name(); - if (currentTag.isEmpty()) - str << tag; - else - str << currentTag; - str << "> in " << context << ", line "<< reader.lineNumber() - << ": " << message; - return result; -} - -static QString msgFallbackWarning(const QXmlStreamReader &reader, const QString &context, - const QString &tag, const QString &location, const QString &identifier, - const QString &fallback) -{ - QString message = QLatin1String("Falling back to \"") - + QDir::toNativeSeparators(fallback) + QLatin1String("\" for \"") + location - + QLatin1Char('"'); - if (!identifier.isEmpty()) - message += QLatin1String(" [") + identifier + QLatin1Char(']'); - return msgTagWarning(reader, context, tag, message); -} - struct QtXmlToSphinx::LinkContext { enum Type @@ -305,7 +278,7 @@ QTextStream &operator<<(QTextStream &str, const QtXmlToSphinx::LinkContext &link } QtXmlToSphinx::QtXmlToSphinx(QtDocGenerator* generator, const QString& doc, const QString& context) - : m_context(context), m_generator(generator), m_insideBold(false), m_insideItalic(false) + : m_tableHasHeader(false), m_context(context), m_generator(generator), m_insideBold(false), m_insideItalic(false) { m_handlerMap.insert(QLatin1String("heading"), &QtXmlToSphinx::handleHeadingTag); m_handlerMap.insert(QLatin1String("brief"), &QtXmlToSphinx::handleParaTag); @@ -1302,7 +1275,7 @@ bool QtXmlToSphinx::convertToRst(QtDocGenerator *generator, QFile sourceFile(sourceFileName); if (!sourceFile.open(QIODevice::ReadOnly | QIODevice::Text)) { if (errorMessage) - *errorMessage = FileOut::msgCannotOpenForReading(sourceFile); + *errorMessage = msgCannotOpenForReading(sourceFile); return false; } const QString doc = QString::fromUtf8(sourceFile.readAll()); @@ -1325,7 +1298,13 @@ void QtXmlToSphinx::Table::normalize() //QDoc3 generates tables with wrong number of columns. We have to //check and if necessary, merge the last columns. - int maxCols = self.at(0).count(); + int maxCols = -1; + for (const auto &row : qAsConst(self)) { + if (row.count() > maxCols) + maxCols = row.count(); + } + if (maxCols <= 0) + return; // add col spans for (row = 0; row < count(); ++row) { for (col = 0; col < at(row).count(); ++col) { @@ -1513,11 +1492,10 @@ QString QtDocGenerator::fileNameForContext(GeneratorContext &context) const const AbstractMetaClass *metaClass = context.metaClass(); if (!context.forSmartPointer()) { return getClassTargetFullName(metaClass, false) + fileNameSuffix(); - } else { - const AbstractMetaType *smartPointerType = context.preciseType(); - QString fileNameBase = getFileNameBaseForSmartPointer(smartPointerType, metaClass); - return fileNameBase + fileNameSuffix(); } + const AbstractMetaType *smartPointerType = context.preciseType(); + QString fileNameBase = getFileNameBaseForSmartPointer(smartPointerType, metaClass); + return fileNameBase + fileNameSuffix(); } void QtDocGenerator::writeFormattedText(QTextStream &s, const Documentation &doc, @@ -1534,7 +1512,7 @@ void QtDocGenerator::writeFormattedText(QTextStream &s, const Documentation &doc } else { const QString &value = doc.value(); const QVector<QStringRef> lines = value.splitRef(QLatin1Char('\n')); - int typesystemIndentation = std::numeric_limits<int>().max(); + int typesystemIndentation = std::numeric_limits<int>::max(); // check how many spaces must be removed from the beginning of each line for (const QStringRef &line : lines) { const auto it = std::find_if(line.cbegin(), line.cend(), @@ -1542,7 +1520,7 @@ void QtDocGenerator::writeFormattedText(QTextStream &s, const Documentation &doc if (it != line.cend()) typesystemIndentation = qMin(typesystemIndentation, int(it - line.cbegin())); } - if (typesystemIndentation == std::numeric_limits<int>().max()) + if (typesystemIndentation == std::numeric_limits<int>::max()) typesystemIndentation = 0; for (const QStringRef &line : lines) { s << INDENT @@ -1677,7 +1655,7 @@ void QtDocGenerator::writeFunctionList(QTextStream& s, const AbstractMetaClass* functionList << str; } - if ((functionList.size() > 0) || (staticFunctionList.size() > 0)) { + if (!functionList.isEmpty() || !staticFunctionList.isEmpty()) { QtXmlToSphinx::Table functionTable; s << endl @@ -1694,7 +1672,7 @@ void QtDocGenerator::writeFunctionList(QTextStream& s, const AbstractMetaClass* void QtDocGenerator::writeFunctionBlock(QTextStream& s, const QString& title, QStringList& functions) { - if (functions.size() > 0) { + if (!functions.isEmpty()) { s << title << endl << QString(title.size(), QLatin1Char('^')) << endl; @@ -1777,7 +1755,8 @@ void QtDocGenerator::writeConstructors(QTextStream& s, const AbstractMetaClass* writeFormattedText(s, func->documentation(), cppClass); } -QString QtDocGenerator::parseArgDocStyle(const AbstractMetaClass* cppClass, const AbstractMetaFunction* func) +QString QtDocGenerator::parseArgDocStyle(const AbstractMetaClass* /* cppClass */, + const AbstractMetaFunction* func) { QString ret; int optArgs = 0; @@ -1859,8 +1838,7 @@ void QtDocGenerator::writeDocSnips(QTextStream &s, if (row.trimmed().size() == 0) { if (currenRow == 0) continue; - else - s << endl; + s << endl; } if (currenRow == 0) { @@ -2124,7 +2102,7 @@ void QtDocGenerator::writeModuleDocumentation() s << ".. module:: " << it.key() << endl << endl; - QString title = it.key(); + const QString &title = it.key(); s << title << endl; s << Pad('*', title.length()) << endl << endl; @@ -2201,7 +2179,7 @@ void QtDocGenerator::writeAdditionalDocumentation() QFile additionalDocumentationFile(m_additionalDocumentationList); if (!additionalDocumentationFile.open(QIODevice::ReadOnly | QIODevice::Text)) { qCWarning(lcShiboken, "%s", - qPrintable(FileOut::msgCannotOpenForReading(additionalDocumentationFile))); + qPrintable(msgCannotOpenForReading(additionalDocumentationFile))); return; } @@ -2261,32 +2239,28 @@ void QtDocGenerator::writeAdditionalDocumentation() successCount, count); } -bool QtDocGenerator::doSetup(const QMap<QString, QString>& args) -{ - m_libSourceDir = args.value(QLatin1String("library-source-dir")); - m_docDataDir = args.value(QLatin1String("documentation-data-dir")); #ifdef __WIN32__ # define PATH_SEP ';' #else # define PATH_SEP ':' #endif - m_codeSnippetDirs = args.value(QLatin1String("documentation-code-snippets-dir"), m_libSourceDir).split(QLatin1Char(PATH_SEP)); - m_extraSectionDir = args.value(QLatin1String("documentation-extra-sections-dir")); - m_docParser = args.value(QLatin1String("doc-parser")) == QLatin1String("doxygen") - ? static_cast<DocParser*>(new DoxygenParser) - : static_cast<DocParser*>(new QtDocParser); - qCDebug(lcShiboken).noquote().nospace() << "doc-parser: " << args.value(QLatin1String("doc-parser")); +bool QtDocGenerator::doSetup() +{ + if (m_codeSnippetDirs.isEmpty()) + m_codeSnippetDirs = m_libSourceDir.split(QLatin1Char(PATH_SEP)); + + if (!m_docParser) + m_docParser = new QtDocParser; if (m_libSourceDir.isEmpty() || m_docDataDir.isEmpty()) { qCWarning(lcShiboken) << "Documentation data dir and/or Qt source dir not informed, " "documentation will not be extracted from Qt sources."; return false; - } else { - m_docParser->setDocumentationDataDirectory(m_docDataDir); - m_docParser->setLibrarySourceDirectory(m_libSourceDir); } - m_additionalDocumentationList = args.value(additionalDocumentationOption()); + + m_docParser->setDocumentationDataDirectory(m_docDataDir); + m_docParser->setLibrarySourceDirectory(m_libSourceDir); return true; } @@ -2294,19 +2268,49 @@ bool QtDocGenerator::doSetup(const QMap<QString, QString>& args) Generator::OptionDescriptions QtDocGenerator::options() const { return OptionDescriptions() - << qMakePair(QLatin1String("doc-parser"), + << qMakePair(QLatin1String("doc-parser=<parser>"), QLatin1String("The documentation parser used to interpret the documentation\n" "input files (qdoc|doxygen)")) - << qMakePair(QLatin1String("documentation-code-snippets-dir"), + << qMakePair(QLatin1String("documentation-code-snippets-dir=<dir>"), QLatin1String("Directory used to search code snippets used by the documentation")) - << qMakePair(QLatin1String("documentation-data-dir"), + << qMakePair(QLatin1String("documentation-data-dir=<dir>"), QLatin1String("Directory with XML files generated by documentation tool")) - << qMakePair(QLatin1String("documentation-extra-sections-dir"), + << qMakePair(QLatin1String("documentation-extra-sections-dir=<dir>"), QLatin1String("Directory used to search for extra documentation sections")) - << qMakePair(QLatin1String("library-source-dir"), + << qMakePair(QLatin1String("library-source-dir=<dir>"), QLatin1String("Directory where library source code is located")) - << qMakePair(additionalDocumentationOption(), + << qMakePair(additionalDocumentationOption() + QLatin1String("=<file>"), QLatin1String("List of additional XML files to be converted to .rst files\n" "(for example, tutorials).")); } +bool QtDocGenerator::handleOption(const QString &key, const QString &value) +{ + if (key == QLatin1String("library-source-dir")) { + m_libSourceDir = value; + return true; + } + if (key == QLatin1String("documentation-data-dir")) { + m_docDataDir = value; + return true; + } + if (key == QLatin1String("documentation-code-snippets-dir")) { + m_codeSnippetDirs = value.split(QLatin1Char(PATH_SEP)); + return true; + } + if (key == QLatin1String("documentation-extra-sections-dir")) { + m_extraSectionDir = value; + return true; + } + if (key == QLatin1String("doc-parser")) { + qCDebug(lcShiboken).noquote().nospace() << "doc-parser: " << value; + if (value == QLatin1String("doxygen")) + m_docParser = new DoxygenParser; + return true; + } + if (key == additionalDocumentationOption()) { + m_additionalDocumentationList = value; + return true; + } + return false; +} diff --git a/sources/shiboken2/generator/qtdoc/qtdocgenerator.h b/sources/shiboken2/generator/qtdoc/qtdocgenerator.h index e467abe90..5545de9a9 100644 --- a/sources/shiboken2/generator/qtdoc/qtdocgenerator.h +++ b/sources/shiboken2/generator/qtdoc/qtdocgenerator.h @@ -209,7 +209,7 @@ public: QString docDataDir() const { return m_docDataDir; } - bool doSetup(const QMap<QString, QString>& args) override; + bool doSetup() override; const char* name() const override { @@ -217,15 +217,15 @@ public: } OptionDescriptions options() const override; + bool handleOption(const QString &key, const QString &value) override; QStringList codeSnippetDirs() const { return m_codeSnippetDirs; } - bool shouldGenerate(const AbstractMetaClass *) const override; - protected: + bool shouldGenerate(const AbstractMetaClass *) const override; QString fileNameSuffix() const override; QString fileNameForContext(GeneratorContext &context) const override; void generateClass(QTextStream &s, GeneratorContext &classContext) override; diff --git a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp index 9e1d6926e..adec70dd7 100644 --- a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp @@ -29,8 +29,10 @@ #include <memory> #include "cppgenerator.h" +#include "fileout.h" #include "overloaddata.h" #include <abstractmetalang.h> +#include <messages.h> #include <reporthandler.h> #include <typedatabase.h> @@ -41,6 +43,8 @@ #include <QtCore/QDebug> #include <QMetaType> +static const char CPP_ARG0[] = "cppArg0"; + QHash<QString, QString> CppGenerator::m_nbFuncs = QHash<QString, QString>(); QHash<QString, QString> CppGenerator::m_sqFuncs = QHash<QString, QString>(); QHash<QString, QString> CppGenerator::m_mpFuncs = QHash<QString, QString>(); @@ -58,6 +62,28 @@ inline AbstractMetaType* getTypeWithoutContainer(AbstractMetaType* arg) return arg; } +// A helper for writing C++ return statements for either void ("return;") +// or some return value ("return value;") +class returnStatement +{ +public: + explicit returnStatement(QString s) : m_returnValue(std::move(s)) {} + + friend QTextStream &operator<<(QTextStream &s, const returnStatement &r); + +private: + const QString m_returnValue; +}; + +QTextStream &operator<<(QTextStream &s, const returnStatement &r) +{ + s << "return"; + if (!r.m_returnValue.isEmpty()) + s << ' ' << r.m_returnValue; + s << ';'; + return s; +} + CppGenerator::CppGenerator() { // Number protocol structure members names @@ -87,27 +113,27 @@ CppGenerator::CppGenerator() m_nbFuncs.insert(QLatin1String("bool"), QLatin1String("nb_nonzero")); // sequence protocol functions - typedef QPair<QString, QString> StrPair; m_sequenceProtocol.insert(QLatin1String("__len__"), - StrPair(QLatin1String("PyObject* " PYTHON_SELF_VAR), QLatin1String("Py_ssize_t"))); + {QLatin1String("PyObject* self"), + QLatin1String("Py_ssize_t")}); m_sequenceProtocol.insert(QLatin1String("__getitem__"), - StrPair(QLatin1String("PyObject* " PYTHON_SELF_VAR ", Py_ssize_t _i"), - QLatin1String("PyObject*"))); + {QLatin1String("PyObject* self, Py_ssize_t _i"), + QLatin1String("PyObject*")}); m_sequenceProtocol.insert(QLatin1String("__setitem__"), - StrPair(QLatin1String("PyObject* " PYTHON_SELF_VAR ", Py_ssize_t _i, PyObject* _value"), - QLatin1String("int"))); + {QLatin1String("PyObject* self, Py_ssize_t _i, PyObject* _value"), + QLatin1String("int")}); m_sequenceProtocol.insert(QLatin1String("__getslice__"), - StrPair(QLatin1String("PyObject* " PYTHON_SELF_VAR ", Py_ssize_t _i1, Py_ssize_t _i2"), - QLatin1String("PyObject*"))); + {QLatin1String("PyObject* self, Py_ssize_t _i1, Py_ssize_t _i2"), + QLatin1String("PyObject*")}); m_sequenceProtocol.insert(QLatin1String("__setslice__"), - StrPair(QLatin1String("PyObject* " PYTHON_SELF_VAR ", Py_ssize_t _i1, Py_ssize_t _i2, PyObject* _value"), - QLatin1String("int"))); + {QLatin1String("PyObject* self, Py_ssize_t _i1, Py_ssize_t _i2, PyObject* _value"), + QLatin1String("int")}); m_sequenceProtocol.insert(QLatin1String("__contains__"), - StrPair(QLatin1String("PyObject* " PYTHON_SELF_VAR ", PyObject* _value"), - QLatin1String("int"))); + {QLatin1String("PyObject* self, PyObject* _value"), + QLatin1String("int")}); m_sequenceProtocol.insert(QLatin1String("__concat__"), - StrPair(QLatin1String("PyObject* " PYTHON_SELF_VAR ", PyObject* _other"), - QLatin1String("PyObject*"))); + {QLatin1String("PyObject* self, PyObject* _other"), + QLatin1String("PyObject*")}); // Sequence protocol structure members names m_sqFuncs.insert(QLatin1String("__concat__"), QLatin1String("sq_concat")); @@ -120,14 +146,14 @@ CppGenerator::CppGenerator() // mapping protocol function m_mappingProtocol.insert(QLatin1String("__mlen__"), - StrPair(QLatin1String("PyObject* " PYTHON_SELF_VAR), - QLatin1String("Py_ssize_t"))); + {QLatin1String("PyObject* self"), + QLatin1String("Py_ssize_t")}); m_mappingProtocol.insert(QLatin1String("__mgetitem__"), - StrPair(QLatin1String("PyObject* " PYTHON_SELF_VAR ", PyObject* _key"), - QLatin1String("PyObject*"))); + {QLatin1String("PyObject* self, PyObject* _key"), + QLatin1String("PyObject*")}); m_mappingProtocol.insert(QLatin1String("__msetitem__"), - StrPair(QLatin1String("PyObject* " PYTHON_SELF_VAR ", PyObject* _key, PyObject* _value"), - QLatin1String("int"))); + {QLatin1String("PyObject* self, PyObject* _key, PyObject* _value"), + QLatin1String("int")}); // Sequence protocol structure members names m_mpFuncs.insert(QLatin1String("__mlen__"), QLatin1String("mp_length")); @@ -186,18 +212,19 @@ QVector<AbstractMetaFunctionList> CppGenerator::filterGroupedOperatorFunctions(c return result; } -bool CppGenerator::hasBoolCast(const AbstractMetaClass* metaClass) const +const AbstractMetaFunction *CppGenerator::boolCast(const AbstractMetaClass* metaClass) const { if (!useIsNullAsNbNonZero()) - return false; + return nullptr; // TODO: This could be configurable someday const AbstractMetaFunction* func = metaClass->findFunction(QLatin1String("isNull")); if (!func || !func->type() || !func->type()->typeEntry()->isPrimitive() || !func->isPublic()) - return false; + return nullptr; const PrimitiveTypeEntry* pte = static_cast<const PrimitiveTypeEntry*>(func->type()->typeEntry()); while (pte->referencedTypeEntry()) pte = pte->referencedTypeEntry(); - return func && func->isConstant() && pte->name() == QLatin1String("bool") && func->arguments().isEmpty(); + return func && func->isConstant() && pte->name() == QLatin1String("bool") + && func->arguments().isEmpty() ? func : nullptr; } typedef QMap<QString, AbstractMetaFunctionList> FunctionGroupMap; @@ -219,6 +246,21 @@ static QString chopType(QString s) return s; } +// Helper for field setters: Check for "const QWidget *" (settable field), +// but not "int *const" (read-only field). +static bool isPointerToConst(const AbstractMetaType *t) +{ + const AbstractMetaType::Indirections &indirections = t->indirectionsV(); + return t->isConstant() && !indirections.isEmpty() + && indirections.constLast() != Indirection::ConstPointer; +} + +static inline bool canGenerateFieldSetter(const AbstractMetaField *field) +{ + const AbstractMetaType *type = field->type(); + return !type->isConstant() || isPointerToConst(type); +} + /*! Function used to write the class generated binding code on the buffer \param s the output buffer @@ -260,6 +302,8 @@ void CppGenerator::generateClass(QTextStream &s, GeneratorContext &classContext) // needs the 'set' class from C++ STL. if (hasMultipleInheritanceInAncestry(metaClass)) s << "#include <set>" << endl; + if (metaClass->generateExceptionHandling()) + s << "#include <exception>" << endl; s << endl << "// module include" << endl << "#include \"" << getModuleHeaderFileName() << '"' << endl; @@ -315,7 +359,7 @@ void CppGenerator::generateClass(QTextStream &s, GeneratorContext &classContext) static_cast<const SmartPointerTypeEntry *>(classContext.preciseType() ->typeEntry()); QString rawGetter = typeEntry->getter(); - s << "static const char * " SMART_POINTER_GETTER " = \"" << rawGetter << "\";"; + s << "static const char * " << SMART_POINTER_GETTER << " = \"" << rawGetter << "\";"; } // class inject-code native/beginning @@ -459,7 +503,7 @@ void CppGenerator::generateClass(QTextStream &s, GeneratorContext &classContext) if (metaClass->typeEntry()->isValue() || metaClass->typeEntry()->isSmartPointer()) { writeCopyFunction(s, classContext); - signatureStream << metaClass->fullName() << ".__copy__()" << endl; + signatureStream << fullPythonClassName(metaClass) << ".__copy__()" << endl; } // Write single method definitions @@ -490,16 +534,20 @@ void CppGenerator::generateClass(QTextStream &s, GeneratorContext &classContext) } } - if (hasBoolCast(metaClass)) { + if (const AbstractMetaFunction *f = boolCast(metaClass)) { ErrorCode errorCode(-1); - s << "static int " << cpythonBaseName(metaClass) << "___nb_bool(PyObject* " PYTHON_SELF_VAR ")" << endl; + s << "static int " << cpythonBaseName(metaClass) << "___nb_bool(PyObject* self)" << endl; s << '{' << endl; writeCppSelfDefinition(s, classContext); - s << INDENT << "int result;" << endl; - s << INDENT << BEGIN_ALLOW_THREADS << endl; - s << INDENT << "result = !" CPP_SELF_VAR "->isNull();" << endl; - s << INDENT << END_ALLOW_THREADS << endl; - s << INDENT << "return result;" << endl; + if (f->allowThread()) { + s << INDENT << "int result;" << endl; + s << INDENT << BEGIN_ALLOW_THREADS << endl; + s << INDENT << "result = !" << CPP_SELF_VAR << "->isNull();" << endl; + s << INDENT << END_ALLOW_THREADS << endl; + s << INDENT << "return result;" << endl; + } else { + s << INDENT << "return !" << CPP_SELF_VAR << "->isNull();" << endl; + } s << '}' << endl << endl; } @@ -546,7 +594,7 @@ void CppGenerator::generateClass(QTextStream &s, GeneratorContext &classContext) if (metaField->isStatic()) continue; writeGetterFunction(s, metaField, classContext); - if (!metaField->type()->isConstant()) + if (canGenerateFieldSetter(metaField)) writeSetterFunction(s, metaField, classContext); s << endl; } @@ -557,10 +605,12 @@ void CppGenerator::generateClass(QTextStream &s, GeneratorContext &classContext) if (metaField->isStatic()) continue; - bool hasSetter = !metaField->type()->isConstant(); s << INDENT << "{const_cast<char*>(\"" << metaField->name() << "\"), "; - s << cpythonGetterFunctionName(metaField); - s << ", " << (hasSetter ? cpythonSetterFunctionName(metaField) : QLatin1String("0")); + s << cpythonGetterFunctionName(metaField) << ", "; + if (canGenerateFieldSetter(metaField)) + s << cpythonSetterFunctionName(metaField); + else + s << '0'; s << "}," << endl; } s << INDENT << "{0} // Sentinel" << endl; @@ -686,7 +736,7 @@ void CppGenerator::writeVirtualMethodNative(QTextStream&s, const AbstractMetaFun Indentation indentation(INDENT); - QString defaultReturnExpr; + DefaultValue defaultReturnExpr; if (retType) { const FunctionModificationList &mods = func->modifications(); for (const FunctionModification &mod : mods) { @@ -694,9 +744,9 @@ void CppGenerator::writeVirtualMethodNative(QTextStream&s, const AbstractMetaFun if (argMod.index == 0 && !argMod.replacedDefaultExpression.isEmpty()) { static const QRegularExpression regex(QStringLiteral("%(\\d+)")); Q_ASSERT(regex.isValid()); - defaultReturnExpr = argMod.replacedDefaultExpression; + QString expr = argMod.replacedDefaultExpression; for (int offset = 0; ; ) { - const QRegularExpressionMatch match = regex.match(defaultReturnExpr, offset); + const QRegularExpressionMatch match = regex.match(expr, offset); if (!match.hasMatch()) break; const int argId = match.capturedRef(1).toInt() - 1; @@ -704,23 +754,27 @@ void CppGenerator::writeVirtualMethodNative(QTextStream&s, const AbstractMetaFun qCWarning(lcShiboken) << "The expression used in return value contains an invalid index."; break; } - defaultReturnExpr.replace(match.captured(0), func->arguments().at(argId)->name()); + expr.replace(match.captured(0), func->arguments().at(argId)->name()); offset = match.capturedStart(1); } + defaultReturnExpr.setType(DefaultValue::Custom); + defaultReturnExpr.setValue(expr); } } } - if (defaultReturnExpr.isEmpty()) + if (!defaultReturnExpr.isValid()) defaultReturnExpr = minimalConstructor(func->type()); - if (defaultReturnExpr.isEmpty()) { + if (!defaultReturnExpr.isValid()) { QString errorMsg = QLatin1String(__FUNCTION__) + QLatin1String(": "); if (const AbstractMetaClass *c = func->implementingClass()) errorMsg += c->qualifiedCppName() + QLatin1String("::"); errorMsg += func->signature(); - errorMsg = ShibokenGenerator::msgCouldNotFindMinimalConstructor(errorMsg, func->type()->cppSignature()); + errorMsg = msgCouldNotFindMinimalConstructor(errorMsg, func->type()->cppSignature()); qCWarning(lcShiboken).noquote().nospace() << errorMsg; s << endl << INDENT << "#error " << errorMsg << endl; } + } else { + defaultReturnExpr.setType(DefaultValue::Void); } if (func->isAbstract() && func->isModifiedRemoved()) { @@ -728,7 +782,7 @@ void CppGenerator::writeVirtualMethodNative(QTextStream&s, const AbstractMetaFun << QString::fromLatin1("Pure virtual method '%1::%2' must be implement but was "\ "completely removed on type system.") .arg(func->ownerClass()->name(), func->minimalSignature()); - s << INDENT << "return " << defaultReturnExpr << ';' << endl; + s << INDENT << returnStatement(defaultReturnExpr.returnValue()) << endl; s << '}' << endl << endl; return; } @@ -747,13 +801,13 @@ void CppGenerator::writeVirtualMethodNative(QTextStream&s, const AbstractMetaFun s << INDENT << "if (PyErr_Occurred())" << endl; { Indentation indentation(INDENT); - s << INDENT << "return " << defaultReturnExpr << ';' << endl; + s << INDENT << returnStatement(defaultReturnExpr.returnValue()) << endl; } - s << INDENT << "Shiboken::AutoDecRef " PYTHON_OVERRIDE_VAR "(Shiboken::BindingManager::instance().getOverride(this, \""; + s << INDENT << "Shiboken::AutoDecRef " << PYTHON_OVERRIDE_VAR << "(Shiboken::BindingManager::instance().getOverride(this, \""; s << funcName << "\"));" << endl; - s << INDENT << "if (" PYTHON_OVERRIDE_VAR ".isNull()) {" << endl; + s << INDENT << "if (" << PYTHON_OVERRIDE_VAR << ".isNull()) {" << endl; { Indentation indentation(INDENT); CodeSnipList snips; @@ -768,7 +822,9 @@ void CppGenerator::writeVirtualMethodNative(QTextStream&s, const AbstractMetaFun s << INDENT << "PyErr_SetString(PyExc_NotImplementedError, \"pure virtual method '"; s << func->ownerClass()->name() << '.' << funcName; s << "()' not implemented.\");" << endl; - s << INDENT << "return " << (retType ? defaultReturnExpr : QString()); + s << INDENT << "return"; + if (retType) + s << ' ' << defaultReturnExpr.returnValue(); } else { s << INDENT << "gil.release();" << endl; s << INDENT; @@ -785,7 +841,7 @@ void CppGenerator::writeVirtualMethodNative(QTextStream&s, const AbstractMetaFun writeConversionRule(s, func, TypeSystem::TargetLangCode); - s << INDENT << "Shiboken::AutoDecRef " PYTHON_ARGS "("; + s << INDENT << "Shiboken::AutoDecRef " << PYTHON_ARGS << "("; if (func->arguments().isEmpty() || allArgumentsRemoved(func)) { s << "PyTuple_New(0));" << endl; @@ -844,7 +900,7 @@ void CppGenerator::writeVirtualMethodNative(QTextStream&s, const AbstractMetaFun if (argMod.resetAfterUse && !invalidateArgs.contains(argMod.index)) { invalidateArgs.insert(argMod.index); s << INDENT << "bool invalidateArg" << argMod.index; - s << " = PyTuple_GET_ITEM(" PYTHON_ARGS ", " << argMod.index - 1 << ")->ob_refcnt == 1;" << endl; + s << " = PyTuple_GET_ITEM(" << PYTHON_ARGS << ", " << argMod.index - 1 << ")->ob_refcnt == 1;" << endl; } else if (argMod.index == 0 && argMod.ownerships[TypeSystem::TargetLangCode] == TypeSystem::CppOwnership) { invalidateReturn = true; } @@ -866,36 +922,36 @@ void CppGenerator::writeVirtualMethodNative(QTextStream&s, const AbstractMetaFun if (!injectedCodeCallsPythonOverride(func)) { s << INDENT; - s << "Shiboken::AutoDecRef " PYTHON_RETURN_VAR "(PyObject_Call(" PYTHON_OVERRIDE_VAR ", " PYTHON_ARGS ", NULL));" << endl; + s << "Shiboken::AutoDecRef " << PYTHON_RETURN_VAR << "(PyObject_Call(" << PYTHON_OVERRIDE_VAR << ", " << PYTHON_ARGS << ", NULL));" << endl; s << INDENT << "// An error happened in python code!" << endl; - s << INDENT << "if (" PYTHON_RETURN_VAR ".isNull()) {" << endl; + s << INDENT << "if (" << PYTHON_RETURN_VAR << ".isNull()) {" << endl; { Indentation indent(INDENT); s << INDENT << "PyErr_Print();" << endl; - s << INDENT << "return " << defaultReturnExpr << ';' << endl; + s << INDENT << returnStatement(defaultReturnExpr.returnValue()) << endl; } s << INDENT << '}' << endl; if (retType) { if (invalidateReturn) - s << INDENT << "bool invalidateArg0 = " PYTHON_RETURN_VAR "->ob_refcnt == 1;" << endl; + s << INDENT << "bool invalidateArg0 = " << PYTHON_RETURN_VAR << "->ob_refcnt == 1;" << endl; if (func->typeReplaced(0) != QLatin1String("PyObject")) { s << INDENT << "// Check return type" << endl; s << INDENT; if (func->typeReplaced(0).isEmpty()) { - s << "PythonToCppFunc " PYTHON_TO_CPP_VAR " = " << cpythonIsConvertibleFunction(func->type()); - s << PYTHON_RETURN_VAR ");" << endl; - s << INDENT << "if (!" PYTHON_TO_CPP_VAR ") {" << endl; + s << "PythonToCppFunc " << PYTHON_TO_CPP_VAR << " = " << cpythonIsConvertibleFunction(func->type()); + s << PYTHON_RETURN_VAR << ");" << endl; + s << INDENT << "if (!" << PYTHON_TO_CPP_VAR << ") {" << endl; { Indentation indent(INDENT); s << INDENT << "Shiboken::warning(PyExc_RuntimeWarning, 2, "\ "\"Invalid return value in function %s, expected %s, got %s.\", \""; s << func->ownerClass()->name() << '.' << funcName << "\", " << getVirtualFunctionReturnTypeName(func); - s << ", Py_TYPE(" PYTHON_RETURN_VAR ")->tp_name);" << endl; - s << INDENT << "return " << defaultReturnExpr << ';' << endl; + s << ", Py_TYPE(" << PYTHON_RETURN_VAR << ")->tp_name);" << endl; + s << INDENT << returnStatement(defaultReturnExpr.returnValue()) << endl; } s << INDENT << '}' << endl; @@ -907,15 +963,16 @@ void CppGenerator::writeVirtualMethodNative(QTextStream&s, const AbstractMetaFun isNumber(func->type()->typeEntry()), func->typeReplaced(0)); s << ';' << endl; s << INDENT << "if (!typeIsValid"; - s << (isPointerToWrapperType(func->type()) ? " && " PYTHON_RETURN_VAR " != Py_None" : ""); + if (isPointerToWrapperType(func->type())) + s << " && " << PYTHON_RETURN_VAR << " != Py_None"; s << ") {" << endl; { Indentation indent(INDENT); s << INDENT << "Shiboken::warning(PyExc_RuntimeWarning, 2, "\ "\"Invalid return value in function %s, expected %s, got %s.\", \""; s << func->ownerClass()->name() << '.' << funcName << "\", " << getVirtualFunctionReturnTypeName(func); - s << ", Py_TYPE(" PYTHON_RETURN_VAR ")->tp_name);" << endl; - s << INDENT << "return " << defaultReturnExpr << ';' << endl; + s << ", Py_TYPE(" << PYTHON_RETURN_VAR << ")->tp_name);" << endl; + s << INDENT << returnStatement(defaultReturnExpr.returnValue()) << endl; } s << INDENT << '}' << endl; @@ -935,12 +992,12 @@ void CppGenerator::writeVirtualMethodNative(QTextStream&s, const AbstractMetaFun if (invalidateReturn) { s << INDENT << "if (invalidateArg0)" << endl; Indentation indentation(INDENT); - s << INDENT << "Shiboken::Object::releaseOwnership(" << PYTHON_RETURN_VAR ".object());" << endl; + s << INDENT << "Shiboken::Object::releaseOwnership(" << PYTHON_RETURN_VAR << ".object());" << endl; } for (int argIndex : qAsConst(invalidateArgs)) { s << INDENT << "if (invalidateArg" << argIndex << ')' << endl; Indentation indentation(INDENT); - s << INDENT << "Shiboken::Object::invalidate(PyTuple_GET_ITEM(" PYTHON_ARGS ", "; + s << INDENT << "Shiboken::Object::invalidate(PyTuple_GET_ITEM(" << PYTHON_ARGS << ", "; s << (argIndex - 1) << "));" << endl; } @@ -950,9 +1007,9 @@ void CppGenerator::writeVirtualMethodNative(QTextStream&s, const AbstractMetaFun for (const ArgumentModification &argMod : funcMod.argument_mods) { if (argMod.ownerships.contains(TypeSystem::NativeCode) && argMod.index == 0 && argMod.ownerships[TypeSystem::NativeCode] == TypeSystem::CppOwnership) { - s << INDENT << "if (Shiboken::Object::checkType(" PYTHON_RETURN_VAR "))" << endl; + s << INDENT << "if (Shiboken::Object::checkType(" << PYTHON_RETURN_VAR << "))" << endl; Indentation indent(INDENT); - s << INDENT << "Shiboken::Object::releaseOwnership(" PYTHON_RETURN_VAR ");" << endl; + s << INDENT << "Shiboken::Object::releaseOwnership(" << PYTHON_RETURN_VAR << ");" << endl; } } } @@ -978,7 +1035,7 @@ void CppGenerator::writeVirtualMethodNative(QTextStream&s, const AbstractMetaFun } if (func->type()->referenceType() == LValueReference && !isPointer(func->type())) s << '*'; - s << CPP_RETURN_VAR ";" << endl; + s << CPP_RETURN_VAR << ';' << endl; } s << '}' << endl << endl; @@ -995,7 +1052,7 @@ void CppGenerator::writeMetaObjectMethod(QTextStream& s, const AbstractMetaClass s << INDENT << "SbkObject* pySelf = Shiboken::BindingManager::instance().retrieveWrapper(this);" << endl; s << INDENT << "if (pySelf == NULL)" << endl; s << INDENT << INDENT << "return " << metaClass->qualifiedCppName() << "::metaObject();" << endl; - s << INDENT << "return PySide::SignalManager::retriveMetaObject(reinterpret_cast<PyObject*>(pySelf));" << endl; + s << INDENT << "return PySide::SignalManager::retrieveMetaObject(reinterpret_cast<PyObject*>(pySelf));" << endl; s << '}' << endl << endl; // qt_metacall function @@ -1259,7 +1316,7 @@ void CppGenerator::writeConverterFunctions(QTextStream &s, const AbstractMetaCla toCppConv = QLatin1Char('*') + cpythonWrapperCPtr(sourceClass->typeEntry(), QLatin1String("pyIn")); } else { // Constructor that does implicit conversion. - if (!conv->typeReplaced(1).isEmpty()) + if (!conv->typeReplaced(1).isEmpty() || conv->isModifiedToArray(1)) continue; const AbstractMetaType* sourceType = conv->arguments().constFirst()->type(); typeCheck = cpythonCheckFunction(sourceType); @@ -1419,7 +1476,7 @@ void CppGenerator::writeConverterRegister(QTextStream &s, const AbstractMetaClas sourceType = buildAbstractMetaTypeFromAbstractMetaClass(conv->ownerClass()); } else { // Constructor that does implicit conversion. - if (!conv->typeReplaced(1).isEmpty()) + if (!conv->typeReplaced(1).isEmpty() || conv->isModifiedToArray(1)) continue; sourceType = conv->arguments().constFirst()->type(); } @@ -1467,7 +1524,7 @@ void CppGenerator::writeMethodWrapperPreamble(QTextStream &s, OverloadData &over // Check if the right constructor was called. if (!ownerClass->hasPrivateDestructor()) { s << INDENT; - s << "if (Shiboken::Object::isUserType(" PYTHON_SELF_VAR ") && !Shiboken::ObjectType::canCallConstructor(" PYTHON_SELF_VAR "->ob_type, Shiboken::SbkType< ::"; + s << "if (Shiboken::Object::isUserType(self) && !Shiboken::ObjectType::canCallConstructor(self->ob_type, Shiboken::SbkType< ::"; QString qualifiedCppName; if (!context.forSmartPointer()) qualifiedCppName = ownerClass->qualifiedCppName(); @@ -1476,7 +1533,7 @@ void CppGenerator::writeMethodWrapperPreamble(QTextStream &s, OverloadData &over s << qualifiedCppName << " >()))" << endl; Indentation indent(INDENT); - s << INDENT << "return " << m_currentErrorCode << ';' << endl << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl << endl; } // Declare pointer for the underlying C++ object. s << INDENT << "::"; @@ -1497,7 +1554,7 @@ void CppGenerator::writeMethodWrapperPreamble(QTextStream &s, OverloadData &over writeCppSelfDefinition(s, rfunc, context, overloadData.hasStaticFunction()); } if (!rfunc->isInplaceOperator() && overloadData.hasNonVoidReturnType()) - s << INDENT << "PyObject* " PYTHON_RETURN_VAR " = 0;" << endl; + s << INDENT << "PyObject* " << PYTHON_RETURN_VAR << " = 0;" << endl; initPythonArguments = minArgs != maxArgs || maxArgs > 1; usesNamedArguments = rfunc->isCallOperator() || overloadData.hasArgumentWithDefaultValue(); @@ -1505,7 +1562,7 @@ void CppGenerator::writeMethodWrapperPreamble(QTextStream &s, OverloadData &over if (maxArgs > 0) { s << INDENT << "int overloadId = -1;" << endl; - s << INDENT << "PythonToCppFunc " PYTHON_TO_CPP_VAR; + s << INDENT << "PythonToCppFunc " << PYTHON_TO_CPP_VAR; if (pythonFunctionWrapperUsesListOfArguments(overloadData)) s << "[] = { 0" << QString::fromLatin1(", 0").repeated(maxArgs-1) << " }"; s << ';' << endl; @@ -1518,7 +1575,7 @@ void CppGenerator::writeMethodWrapperPreamble(QTextStream &s, OverloadData &over if (initPythonArguments) { s << INDENT << "int numArgs = "; if (minArgs == 0 && maxArgs == 1 && !rfunc->isConstructor() && !pythonFunctionWrapperUsesListOfArguments(overloadData)) - s << "(" PYTHON_ARG " == 0 ? 0 : 1);" << endl; + s << "(" << PYTHON_ARG << " == 0 ? 0 : 1);" << endl; else writeArgumentsInitializer(s, overloadData); } @@ -1534,7 +1591,7 @@ void CppGenerator::writeConstructorWrapper(QTextStream &s, const AbstractMetaFun const AbstractMetaClass* metaClass = rfunc->ownerClass(); s << "static int" << endl; - s << cpythonFunctionName(rfunc) << "(PyObject* " PYTHON_SELF_VAR ", PyObject* args, PyObject* kwds)" << endl; + s << cpythonFunctionName(rfunc) << "(PyObject* self, PyObject* args, PyObject* kwds)" << endl; s << '{' << endl; QSet<QString> argNamesSet; @@ -1560,10 +1617,10 @@ void CppGenerator::writeConstructorWrapper(QTextStream &s, const AbstractMetaFun s << INDENT << "const QMetaObject* metaObject;" << endl; } - s << INDENT << "SbkObject* sbkSelf = reinterpret_cast<SbkObject*>(" PYTHON_SELF_VAR ");" << endl; + s << INDENT << "SbkObject* sbkSelf = reinterpret_cast<SbkObject*>(self);" << endl; if (metaClass->isAbstract() || metaClass->baseClassNames().size() > 1) { - s << INDENT << "SbkObjectType* type = reinterpret_cast<SbkObjectType*>(" PYTHON_SELF_VAR "->ob_type);" << endl; + s << INDENT << "SbkObjectType* type = reinterpret_cast<SbkObjectType*>(self->ob_type);" << endl; s << INDENT << "SbkObjectType* myType = reinterpret_cast<SbkObjectType*>(" << cpythonTypeNameExt(metaClass->typeEntry()) << ");" << endl; } @@ -1577,7 +1634,7 @@ void CppGenerator::writeConstructorWrapper(QTextStream &s, const AbstractMetaFun s << INDENT << "\"'" << metaClass->qualifiedCppName(); } s << "' represents a C++ abstract class and cannot be instantiated\");" << endl; - s << INDENT << "return " << m_currentErrorCode << ';' << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl; } s << INDENT << '}' << endl << endl; } @@ -1608,7 +1665,7 @@ void CppGenerator::writeConstructorWrapper(QTextStream &s, const AbstractMetaFun { Indentation indent(INDENT); s << INDENT << "delete cptr;" << endl; - s << INDENT << "return " << m_currentErrorCode << ';' << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl; } s << INDENT << '}' << endl; if (overloadData.maxArgs() > 0) { @@ -1636,12 +1693,12 @@ void CppGenerator::writeConstructorWrapper(QTextStream &s, const AbstractMetaFun // Create metaObject and register signal/slot if (metaClass->isQObject() && usePySideExtensions()) { s << endl << INDENT << "// QObject setup" << endl; - s << INDENT << "PySide::Signal::updateSourceObject(" PYTHON_SELF_VAR ");" << endl; + s << INDENT << "PySide::Signal::updateSourceObject(self);" << endl; s << INDENT << "metaObject = cptr->metaObject(); // <- init python qt properties" << endl; - s << INDENT << "if (kwds && !PySide::fillQtProperties(" PYTHON_SELF_VAR ", metaObject, kwds, argNames, " << argNamesSet.count() << "))" << endl; + s << INDENT << "if (kwds && !PySide::fillQtProperties(self, metaObject, kwds, argNames, " << argNamesSet.count() << "))" << endl; { Indentation indentation(INDENT); - s << INDENT << "return " << m_currentErrorCode << ';' << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl; } } @@ -1694,7 +1751,7 @@ void CppGenerator::writeMethodWrapper(QTextStream &s, const AbstractMetaFunction int maxArgs = overloadData.maxArgs(); s << "static PyObject* "; - s << cpythonFunctionName(rfunc) << "(PyObject* " PYTHON_SELF_VAR; + s << cpythonFunctionName(rfunc) << "(PyObject* self"; if (maxArgs > 0) { s << ", PyObject* " << (pythonFunctionWrapperUsesListOfArguments(overloadData) ? "args" : PYTHON_ARG); if (overloadData.hasArgumentWithDefaultValue() || rfunc->isCallOperator()) @@ -1707,6 +1764,7 @@ void CppGenerator::writeMethodWrapper(QTextStream &s, const AbstractMetaFunction s << endl; /* + * This code is intended for shift operations only: * Make sure reverse <</>> operators defined in other classes (specially from other modules) * are called. A proper and generic solution would require an reengineering in the operator * system like the extended converters. @@ -1721,30 +1779,32 @@ void CppGenerator::writeMethodWrapper(QTextStream &s, const AbstractMetaFunction && rfunc->isOperatorOverload(); if (callExtendedReverseOperator) { QString revOpName = ShibokenGenerator::pythonOperatorFunctionName(rfunc).insert(2, QLatin1Char('r')); - if (rfunc->isBinaryOperator()) { + // For custom classes, operations like __radd__ and __rmul__ + // will enter an infinite loop. + if (rfunc->isBinaryOperator() && revOpName.contains(QLatin1String("shift"))) { s << INDENT << "if (!isReverse" << endl; { Indentation indent(INDENT); - s << INDENT << "&& Shiboken::Object::checkType(" PYTHON_ARG ")" << endl; - s << INDENT << "&& !PyObject_TypeCheck(" PYTHON_ARG ", " PYTHON_SELF_VAR "->ob_type)" << endl; - s << INDENT << "&& PyObject_HasAttrString(" PYTHON_ARG ", const_cast<char*>(\"" << revOpName << "\"))) {" << endl; + s << INDENT << "&& Shiboken::Object::checkType(" << PYTHON_ARG << ")" << endl; + s << INDENT << "&& !PyObject_TypeCheck(" << PYTHON_ARG << ", self->ob_type)" << endl; + s << INDENT << "&& PyObject_HasAttrString(" << PYTHON_ARG << ", const_cast<char*>(\"" << revOpName << "\"))) {" << endl; // This PyObject_CallMethod call will emit lots of warnings like // "deprecated conversion from string constant to char *" during compilation // due to the method name argument being declared as "char*" instead of "const char*" // issue 6952 http://bugs.python.org/issue6952 - s << INDENT << "PyObject* revOpMethod = PyObject_GetAttrString(" PYTHON_ARG ", const_cast<char*>(\"" << revOpName << "\"));" << endl; + s << INDENT << "PyObject* revOpMethod = PyObject_GetAttrString(" << PYTHON_ARG << ", const_cast<char*>(\"" << revOpName << "\"));" << endl; s << INDENT << "if (revOpMethod && PyCallable_Check(revOpMethod)) {" << endl; { Indentation indent(INDENT); - s << INDENT << PYTHON_RETURN_VAR " = PyObject_CallFunction(revOpMethod, const_cast<char*>(\"O\"), " PYTHON_SELF_VAR ");" << endl; + s << INDENT << PYTHON_RETURN_VAR << " = PyObject_CallFunction(revOpMethod, const_cast<char*>(\"O\"), self);" << endl; s << INDENT << "if (PyErr_Occurred() && (PyErr_ExceptionMatches(PyExc_NotImplementedError)"; s << " || PyErr_ExceptionMatches(PyExc_AttributeError))) {" << endl; { Indentation indent(INDENT); s << INDENT << "PyErr_Clear();" << endl; - s << INDENT << "Py_XDECREF(" PYTHON_RETURN_VAR ");" << endl; - s << INDENT << PYTHON_RETURN_VAR " = 0;" << endl; + s << INDENT << "Py_XDECREF(" << PYTHON_RETURN_VAR << ");" << endl; + s << INDENT << PYTHON_RETURN_VAR << " = 0;" << endl; } s << INDENT << '}' << endl; } @@ -1754,7 +1814,7 @@ void CppGenerator::writeMethodWrapper(QTextStream &s, const AbstractMetaFunction s << INDENT << "}" << endl; } s << INDENT << "// Do not enter here if other object has implemented a reverse operator." << endl; - s << INDENT << "if (!" PYTHON_RETURN_VAR ") {" << endl << endl; + s << INDENT << "if (!" << PYTHON_RETURN_VAR << ") {" << endl << endl; } if (maxArgs > 0) @@ -1763,7 +1823,7 @@ void CppGenerator::writeMethodWrapper(QTextStream &s, const AbstractMetaFunction writeFunctionCalls(s, overloadData, classContext); if (callExtendedReverseOperator) - s << endl << INDENT << "} // End of \"if (!" PYTHON_RETURN_VAR ")\"" << endl; + s << endl << INDENT << "} // End of \"if (!" << PYTHON_RETURN_VAR << ")\"" << endl; s << endl; @@ -1771,10 +1831,10 @@ void CppGenerator::writeMethodWrapper(QTextStream &s, const AbstractMetaFunction if (hasReturnValue) { if (rfunc->isInplaceOperator()) { - s << INDENT << "Py_INCREF(" PYTHON_SELF_VAR ");\n"; - s << INDENT << "return " PYTHON_SELF_VAR ";\n"; + s << INDENT << "Py_INCREF(self);\n"; + s << INDENT << "return self;\n"; } else { - s << INDENT << "return " PYTHON_RETURN_VAR ";\n"; + s << INDENT << "return " << PYTHON_RETURN_VAR << ";\n"; } } else { s << INDENT << "Py_RETURN_NONE;" << endl; @@ -1795,7 +1855,7 @@ void CppGenerator::writeArgumentsInitializer(QTextStream& s, OverloadData& overl int maxArgs = overloadData.maxArgs(); s << INDENT << "PyObject* "; - s << PYTHON_ARGS "[] = {" + s << PYTHON_ARGS << "[] = {" << QString(maxArgs, QLatin1Char('0')).split(QLatin1String(""), QString::SkipEmptyParts).join(QLatin1String(", ")) << "};" << endl; s << endl; @@ -1807,8 +1867,8 @@ void CppGenerator::writeArgumentsInitializer(QTextStream& s, OverloadData& overl s << INDENT << "PyObject* nonvarargs = PyTuple_GetSlice(args, 0, " << maxArgs << ");" << endl; s << INDENT << "Shiboken::AutoDecRef auto_nonvarargs(nonvarargs);" << endl; - s << INDENT << PYTHON_ARGS "[" << maxArgs << "] = PyTuple_GetSlice(args, " << maxArgs << ", numArgs);" << endl; - s << INDENT << "Shiboken::AutoDecRef auto_varargs(" PYTHON_ARGS "[" << maxArgs << "]);" << endl; + s << INDENT << PYTHON_ARGS << '[' << maxArgs << "] = PyTuple_GetSlice(args, " << maxArgs << ", numArgs);" << endl; + s << INDENT << "Shiboken::AutoDecRef auto_varargs(" << PYTHON_ARGS << "[" << maxArgs << "]);" << endl; s << endl; } @@ -1822,7 +1882,7 @@ void CppGenerator::writeArgumentsInitializer(QTextStream& s, OverloadData& overl { Indentation indent(INDENT); s << INDENT << "PyErr_SetString(PyExc_TypeError, \"" << fullPythonFunctionName(rfunc) << "(): too many arguments\");" << endl; - s << INDENT << "return " << m_currentErrorCode << ';' << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl; } s << INDENT << '}'; } @@ -1835,7 +1895,7 @@ void CppGenerator::writeArgumentsInitializer(QTextStream& s, OverloadData& overl { Indentation indent(INDENT); s << INDENT << "PyErr_SetString(PyExc_TypeError, \"" << fullPythonFunctionName(rfunc) << "(): not enough arguments\");" << endl; - s << INDENT << "return " << m_currentErrorCode << ';' << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl; } s << INDENT << '}'; } @@ -1867,13 +1927,12 @@ void CppGenerator::writeArgumentsInitializer(QTextStream& s, OverloadData& overl s << "PyArg_ParseTuple(" << argsVar << ", \"|" << QByteArray(maxArgs, 'O') << ':' << funcName << '"'; else s << "PyArg_UnpackTuple(" << argsVar << ", \"" << funcName << "\", " << minArgs << ", " << maxArgs; - QStringList palist; for (int i = 0; i < maxArgs; i++) - palist << QString::fromLatin1("&(" PYTHON_ARGS "[%1])").arg(i); - s << ", " << palist.join(QLatin1String(", ")) << "))" << endl; + s << ", &(" << PYTHON_ARGS << '[' << i << "])"; + s << "))" << endl; { Indentation indent(INDENT); - s << INDENT << "return " << m_currentErrorCode << ';' << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl; } s << endl; } @@ -1895,7 +1954,7 @@ void CppGenerator::writeCppSelfDefinition(QTextStream &s, } QString cppSelfAttribution; - QString pythonSelfVar = QLatin1String(PYTHON_SELF_VAR); + QString pythonSelfVar = QLatin1String("self"); QString cpythonWrapperCPtrResult; if (!context.forSmartPointer()) cpythonWrapperCPtrResult = cpythonWrapperCPtr(metaClass, pythonSelfVar); @@ -1908,7 +1967,7 @@ void CppGenerator::writeCppSelfDefinition(QTextStream &s, .arg(className, QLatin1String(CPP_SELF_VAR), cast, cpythonWrapperCPtrResult); } else { - s << INDENT << className << "* " CPP_SELF_VAR " = 0;" << endl; + s << INDENT << className << "* " << CPP_SELF_VAR << " = 0;" << endl; writeUnusedVariableCast(s, QLatin1String(CPP_SELF_VAR)); cppSelfAttribution = QString::fromLatin1("%1 = %2%3") .arg(QLatin1String(CPP_SELF_VAR), @@ -1918,17 +1977,17 @@ void CppGenerator::writeCppSelfDefinition(QTextStream &s, // Checks if the underlying C++ object is valid. if (hasStaticOverload && !cppSelfAsReference) { - s << INDENT << "if (" PYTHON_SELF_VAR ") {" << endl; + s << INDENT << "if (self) {" << endl; { Indentation indent(INDENT); - writeInvalidPyObjectCheck(s, QLatin1String(PYTHON_SELF_VAR)); + writeInvalidPyObjectCheck(s, QLatin1String("self")); s << INDENT << cppSelfAttribution << ';' << endl; } s << INDENT << '}' << endl; return; } - writeInvalidPyObjectCheck(s, QLatin1String(PYTHON_SELF_VAR)); + writeInvalidPyObjectCheck(s, QLatin1String("self")); s << INDENT << cppSelfAttribution << ';' << endl; } @@ -1942,17 +2001,17 @@ void CppGenerator::writeCppSelfDefinition(QTextStream &s, if (func->isOperatorOverload() && func->isBinaryOperator()) { QString checkFunc = cpythonCheckFunction(func->ownerClass()->typeEntry()); - s << INDENT << "bool isReverse = " << checkFunc << PYTHON_ARG ")" << endl; + s << INDENT << "bool isReverse = " << checkFunc << PYTHON_ARG << ')' << endl; { Indentation indent1(INDENT); Indentation indent2(INDENT); Indentation indent3(INDENT); Indentation indent4(INDENT); - s << INDENT << "&& !" << checkFunc << PYTHON_SELF_VAR ");" << endl; + s << INDENT << "&& !" << checkFunc << "self);" << endl; } s << INDENT << "if (isReverse)" << endl; Indentation indent(INDENT); - s << INDENT << "std::swap(" PYTHON_SELF_VAR ", " PYTHON_ARG ");" << endl; + s << INDENT << "std::swap(self, " << PYTHON_ARG << ");" << endl; } writeCppSelfDefinition(s, context, hasStaticOverload); @@ -2053,17 +2112,20 @@ void CppGenerator::writeErrorSection(QTextStream& s, OverloadData& overloadData) << ", 0};" << endl; s << INDENT << "Shiboken::setErrorAboutWrongArguments(" << argsVar << ", \"" << funcName << "\", overloads);" << endl; } - s << INDENT << "return " << m_currentErrorCode << ';' << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl; } void CppGenerator::writeFunctionReturnErrorCheckSection(QTextStream& s, bool hasReturnValue) { - s << INDENT << "if (PyErr_Occurred()" << (hasReturnValue ? " || !" PYTHON_RETURN_VAR : "") << ") {" << endl; + s << INDENT << "if (PyErr_Occurred()"; + if (hasReturnValue) + s << " || !" << PYTHON_RETURN_VAR; + s << ") {" << endl; { Indentation indent(INDENT); if (hasReturnValue) - s << INDENT << "Py_XDECREF(" PYTHON_RETURN_VAR ");" << endl; - s << INDENT << "return " << m_currentErrorCode << ';' << endl; + s << INDENT << "Py_XDECREF(" << PYTHON_RETURN_VAR << ");" << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl; } s << INDENT << '}' << endl; } @@ -2072,12 +2134,13 @@ void CppGenerator::writeInvalidPyObjectCheck(QTextStream& s, const QString& pyOb { s << INDENT << "if (!Shiboken::Object::isValid(" << pyObj << "))" << endl; Indentation indent(INDENT); - s << INDENT << "return " << m_currentErrorCode << ';' << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl; } static QString pythonToCppConverterForArgumentName(const QString& argumentName) { - static const QRegularExpression pyArgsRegex(QLatin1String(PYTHON_ARGS"(\\[\\d+[-]?\\d*\\])")); + static const QRegularExpression pyArgsRegex(QLatin1String(PYTHON_ARGS) + + QLatin1String(R"((\[\d+[-]?\d*\]))")); Q_ASSERT(pyArgsRegex.isValid()); const QRegularExpressionMatch match = pyArgsRegex.match(argumentName); QString result = QLatin1String(PYTHON_TO_CPP_VAR); @@ -2379,7 +2442,7 @@ void CppGenerator::writeConversionRule(QTextStream& s, const AbstractMetaFunctio void CppGenerator::writeNoneReturn(QTextStream& s, const AbstractMetaFunction* func, bool thereIsReturnValue) { if (thereIsReturnValue && (!func->type() || func->argumentRemoved(0)) && !injectedCodeHasReturnValueAttribution(func)) { - s << INDENT << PYTHON_RETURN_VAR " = Py_None;" << endl; + s << INDENT << PYTHON_RETURN_VAR << " = Py_None;" << endl; s << INDENT << "Py_INCREF(Py_None);" << endl; } } @@ -2493,8 +2556,9 @@ void CppGenerator::writeOverloadedFunctionDecisorEngine(QTextStream& s, const Ov const AbstractMetaFunction* refFunc = overloadData->referenceFunction(); QStringList typeChecks; + QString pyArgName = (usePyArgs && maxArgs > 1) - ? QString::fromLatin1(PYTHON_ARGS "[%1]").arg(overloadData->argPos()) + ? pythonArgsAt(overloadData->argPos()) : QLatin1String(PYTHON_ARG); OverloadData* od = overloadData; int startArg = od->argPos(); @@ -2503,7 +2567,7 @@ void CppGenerator::writeOverloadedFunctionDecisorEngine(QTextStream& s, const Ov bool typeReplacedByPyObject = od->argumentTypeReplaced() == QLatin1String("PyObject"); if (!typeReplacedByPyObject) { if (usePyArgs) - pyArgName = QString::fromLatin1(PYTHON_ARGS "[%1]").arg(od->argPos()); + pyArgName = pythonArgsAt(od->argPos()); QString typeCheck; QTextStream tck(&typeCheck); const AbstractMetaFunction* func = od->referenceFunction(); @@ -2613,7 +2677,7 @@ void CppGenerator::writeSingleFunctionCall(QTextStream &s, s << INDENT << "PyErr_Format(PyExc_TypeError, \"%s is a private method.\", \"" << func->signature().replace(QLatin1String("::"), QLatin1String(".")) << "\");" << endl; - s << INDENT << "return " << m_currentErrorCode << ';' << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl; return; } @@ -2630,7 +2694,8 @@ void CppGenerator::writeSingleFunctionCall(QTextStream &s, const AbstractMetaArgument* arg = func->arguments().at(argIdx); if (func->argumentRemoved(argIdx + 1)) { if (!arg->defaultValueExpression().isEmpty()) { - QString cppArgRemoved = QString::fromLatin1(CPP_ARG_REMOVED "%1").arg(argIdx); + const QString cppArgRemoved = QLatin1String(CPP_ARG_REMOVED) + + QString::number(argIdx); s << INDENT << getFullTypeName(arg->type()) << ' ' << cppArgRemoved; s << " = " << guessScopeForDefaultValue(func, arg) << ';' << endl; writeUnusedVariableCast(s, cppArgRemoved); @@ -2649,8 +2714,8 @@ void CppGenerator::writeSingleFunctionCall(QTextStream &s, if (!argType || (mayHaveUnunsedArguments && !injectedCodeUsesArgument(func, argIdx))) continue; int argPos = argIdx - removedArgs; - QString argName = QString::fromLatin1(CPP_ARG"%1").arg(argPos); - QString pyArgName = usePyArgs ? QString::fromLatin1(PYTHON_ARGS "[%1]").arg(argPos) : QLatin1String(PYTHON_ARG); + QString argName = QLatin1String(CPP_ARG) + QString::number(argPos); + QString pyArgName = usePyArgs ? pythonArgsAt(argPos) : QLatin1String(PYTHON_ARG); QString defaultValue = guessScopeForDefaultValue(func, arg); writeArgumentConversion(s, argType, argName, pyArgName, func->implementingClass(), defaultValue, func->isUserAdded()); } @@ -2896,10 +2961,8 @@ void CppGenerator::writePythonToCppConversionFunctions(QTextStream& s, const Abs const AbstractMetaType* type = containerType->instantiations().at(i); QString typeName = getFullTypeName(type); if (type->isValue() && isValueTypeWithCopyConstructorOnly(type)) { - static const QRegularExpression regex(QLatin1String(CONVERTTOCPP_REGEX)); - Q_ASSERT(regex.isValid()); for (int pos = 0; ; ) { - const QRegularExpressionMatch match = regex.match(code, pos); + const QRegularExpressionMatch match = convertToCppRegEx().match(code, pos); if (!match.hasMatch()) break; pos = match.capturedEnd(); @@ -2954,15 +3017,13 @@ void CppGenerator::writeNamedArgumentResolution(QTextStream& s, const AbstractMe s << INDENT << "PyObject* "; for (const AbstractMetaArgument *arg : args) { int pyArgIndex = arg->argumentIndex() - OverloadData::numberOfRemovedArguments(func, arg->argumentIndex()); - QString pyArgName = usePyArgs - ? QString::fromLatin1(PYTHON_ARGS "[%1]").arg(pyArgIndex) - : QLatin1String(PYTHON_ARG); + QString pyArgName = usePyArgs ? pythonArgsAt(pyArgIndex) : QLatin1String(PYTHON_ARG); s << "value = PyDict_GetItemString(kwds, \"" << arg->name() << "\");" << endl; s << INDENT << "if (value && " << pyArgName << ") {" << endl; { Indentation indent(INDENT); s << INDENT << pyErrString.arg(arg->name()) << endl; - s << INDENT << "return " << m_currentErrorCode << ';' << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl; } s << INDENT << "} else if (value) {" << endl; { @@ -2989,7 +3050,7 @@ QString CppGenerator::argumentNameFromIndex(const AbstractMetaFunction* func, in *wrappedClass = 0; QString pyArgName; if (argIndex == -1) { - pyArgName = QLatin1String(PYTHON_SELF_VAR); + pyArgName = QLatin1String("self"); *wrappedClass = func->implementingClass(); } else if (argIndex == 0) { AbstractMetaType *funcType = func->type(); @@ -3016,12 +3077,23 @@ QString CppGenerator::argumentNameFromIndex(const AbstractMetaFunction* func, in && OverloadData::isSingleArgument(getFunctionGroups(func->implementingClass())[func->name()])) pyArgName = QLatin1String(PYTHON_ARG); else - pyArgName = QString::fromLatin1(PYTHON_ARGS "[%1]").arg(argIndex - 1); + pyArgName = pythonArgsAt(argIndex - 1); } } return pyArgName; } +static QStringList defaultExceptionHandling() +{ + static const QStringList result{ + QLatin1String("} catch (const std::exception &e) {"), + QLatin1String(" PyErr_SetString(PyExc_RuntimeError, e.what());"), + QLatin1String("} catch (...) {"), + QLatin1String(" PyErr_SetString(PyExc_RuntimeError, \"An unknown exception was caught\");"), + QLatin1String("}")}; + return result; +} + void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *func, GeneratorContext &context, int maxArgs) { @@ -3037,12 +3109,12 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f } if (func->isAbstract()) { - s << INDENT << "if (Shiboken::Object::hasCppWrapper(reinterpret_cast<SbkObject*>(" PYTHON_SELF_VAR "))) {\n"; + s << INDENT << "if (Shiboken::Object::hasCppWrapper(reinterpret_cast<SbkObject*>(self))) {\n"; { Indentation indent(INDENT); s << INDENT << "PyErr_SetString(PyExc_NotImplementedError, \"pure virtual method '"; s << func->ownerClass()->name() << '.' << func->name() << "()' not implemented.\");" << endl; - s << INDENT << "return " << m_currentErrorCode << ';' << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl; } s << INDENT << "}\n"; } @@ -3091,16 +3163,21 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f if (hasConversionRule) userArgs << arg->name() + QLatin1String(CONV_RULE_OUT_VAR_SUFFIX); else if (!arg->defaultValueExpression().isEmpty()) - userArgs << QString::fromLatin1(CPP_ARG_REMOVED "%1").arg(i); + userArgs.append(QLatin1String(CPP_ARG_REMOVED) + QString::number(i)); } else { int idx = arg->argumentIndex() - removedArgs; bool deRef = isValueTypeWithCopyConstructorOnly(arg->type()) || isObjectTypeUsedAsValueType(arg->type()) || (arg->type()->referenceType() == LValueReference && isWrapperType(arg->type()) && !isPointer(arg->type())); - QString argName = hasConversionRule - ? arg->name() + QLatin1String(CONV_RULE_OUT_VAR_SUFFIX) - : QString::fromLatin1("%1" CPP_ARG "%2").arg(deRef ? QLatin1String("*") : QString()).arg(idx); - userArgs << argName; + if (hasConversionRule) { + userArgs.append(arg->name() + QLatin1String(CONV_RULE_OUT_VAR_SUFFIX)); + } else { + QString argName; + if (deRef) + argName += QLatin1Char('*'); + argName += QLatin1String(CPP_ARG) + QString::number(idx); + userArgs.append(argName); + } } } @@ -3113,17 +3190,16 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f bool argsClear = true; for (int i = func->arguments().size() - 1; i >= maxArgs + removedArgs; i--) { const AbstractMetaArgument* arg = func->arguments().at(i); - bool defValModified = arg->defaultValueExpression() != arg->originalDefaultValueExpression(); + const bool defValModified = arg->hasModifiedDefaultValueExpression(); bool hasConversionRule = !func->conversionRule(TypeSystem::NativeCode, arg->argumentIndex() + 1).isEmpty(); if (argsClear && !defValModified && !hasConversionRule) continue; - else - argsClear = false; + argsClear = false; otherArgsModified |= defValModified || hasConversionRule || func->argumentRemoved(i + 1); if (hasConversionRule) otherArgs.prepend(arg->name() + QLatin1String(CONV_RULE_OUT_VAR_SUFFIX)); else - otherArgs.prepend(QString::fromLatin1(CPP_ARG_REMOVED "%1").arg(i)); + otherArgs.prepend(QLatin1String(CPP_ARG_REMOVED) + QString::number(i)); } if (otherArgsModified) userArgs << otherArgs; @@ -3135,10 +3211,11 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f QString useVAddr; QTextStream uva(&useVAddr); if (func->isOperatorOverload() && !func->isCallOperator()) { - QString firstArg = QLatin1String("(*" CPP_SELF_VAR ")"); - if (func->isPointerOperator()) - firstArg.remove(1, 1); // remove the de-reference operator - + QString firstArg(QLatin1Char('(')); + if (!func->isPointerOperator()) // no de-reference operator + firstArg += QLatin1Char('*'); + firstArg += QLatin1String(CPP_SELF_VAR); + firstArg += QLatin1Char(')'); QString secondArg = QLatin1String(CPP_ARG0); if (!func->isUnaryOperator() && shouldDereferenceArgumentPointer(func->arguments().constFirst())) { secondArg.prepend(QLatin1String("(*")); @@ -3211,7 +3288,8 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f } else { const QString selfVarCast = func->ownerClass() == func->implementingClass() ? QLatin1String(CPP_SELF_VAR) - : QLatin1String("reinterpret_cast<") + methodCallClassName + QLatin1String(" *>(" CPP_SELF_VAR ")"); + : QLatin1String("reinterpret_cast<") + methodCallClassName + + QLatin1String(" *>(") + QLatin1String(CPP_SELF_VAR) + QLatin1Char(')'); if (func->isConstant()) { if (avoidProtectedHack()) { mc << "const_cast<const ::"; @@ -3219,7 +3297,8 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f // PYSIDE-500: Need a special wrapper cast when inherited const QString selfWrapCast = func->ownerClass() == func->implementingClass() ? QLatin1String(CPP_SELF_VAR) - : QLatin1String("reinterpret_cast<") + wrapperName(func->ownerClass()) + QLatin1String(" *>(" CPP_SELF_VAR ")"); + : QLatin1String("reinterpret_cast<") + wrapperName(func->ownerClass()) + + QLatin1String(" *>(") + QLatin1String(CPP_SELF_VAR) + QLatin1Char(')'); mc << wrapperName(func->ownerClass()); mc << "*>(" << selfWrapCast << ")->"; } @@ -3263,7 +3342,7 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f methodCallClassName); normalCall.remove(QLatin1String("::%CLASS_NAME::")); methodCall.clear(); - mc << "Shiboken::Object::hasCppWrapper(reinterpret_cast<SbkObject*>(" PYTHON_SELF_VAR ")) ? "; + mc << "Shiboken::Object::hasCppWrapper(reinterpret_cast<SbkObject*>(self)) ? "; mc << virtualCall << " : " << normalCall; } } @@ -3271,7 +3350,19 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f } if (!injectedCodeCallsCppFunction(func)) { - s << INDENT << BEGIN_ALLOW_THREADS << endl << INDENT; + const bool allowThread = func->allowThread(); + const bool generateExceptionHandling = func->generateExceptionHandling(); + if (generateExceptionHandling) { + s << INDENT << "try {\n"; + ++INDENT.indent; + if (allowThread) { + s << INDENT << "Shiboken::ThreadStateSaver threadSaver;\n" + << INDENT << "threadSaver.save();\n"; + } + } else if (allowThread) { + s << INDENT << BEGIN_ALLOW_THREADS << endl; + } + s << INDENT; if (isCtor) { s << (useVAddr.isEmpty() ? QString::fromLatin1("cptr = %1;").arg(methodCall) : useVAddr) << endl; @@ -3299,18 +3390,23 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f methodCall.append(QLatin1Char(')')); } } - s << " " CPP_RETURN_VAR " = "; + s << " " << CPP_RETURN_VAR << " = "; s << methodCall << ';' << endl; } else { s << methodCall << ';' << endl; } - s << INDENT << END_ALLOW_THREADS << endl; + if (allowThread) { + s << INDENT << (generateExceptionHandling + ? "threadSaver.restore();" : END_ALLOW_THREADS) << '\n'; + } + + // Convert result if (!func->conversionRule(TypeSystem::TargetLangCode, 0).isEmpty()) { writeConversionRule(s, func, TypeSystem::TargetLangCode, QLatin1String(PYTHON_RETURN_VAR)); } else if (!isCtor && !func->isInplaceOperator() && func->type() && !injectedCodeHasReturnValueAttribution(func, TypeSystem::TargetLangCode)) { - s << INDENT << PYTHON_RETURN_VAR " = "; + s << INDENT << PYTHON_RETURN_VAR << " = "; if (isObjectTypeUsedAsValueType(func->type())) { s << "Shiboken::Object::newObject(reinterpret_cast<SbkObjectType *>(" << cpythonTypeNameExt(func->type()->typeEntry()) << "), " << CPP_RETURN_VAR << ", true, true)"; @@ -3319,6 +3415,13 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f } s << ';' << endl; } + + if (generateExceptionHandling) { // "catch" code + --INDENT.indent; + const QStringList handlingCode = defaultExceptionHandling(); + for (const auto &line : handlingCode) + s << INDENT << line << '\n'; + } } } @@ -3370,7 +3473,7 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f s << "getOwnership(" << pyArgName << ");"; } else if (wrappedClass->hasVirtualDestructor()) { if (arg_mod.index == 0) - s << "releaseOwnership(" PYTHON_RETURN_VAR ");"; + s << "releaseOwnership(" << PYTHON_RETURN_VAR << ");"; else s << "releaseOwnership(" << pyArgName << ");"; } else { @@ -3406,10 +3509,10 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f else s << INDENT << "Shiboken::Object::removeReference("; - s << "reinterpret_cast<SbkObject*>(" PYTHON_SELF_VAR "), \""; + s << "reinterpret_cast<SbkObject*>(self), \""; QString varName = arg_mod.referenceCounts.constFirst().varName; if (varName.isEmpty()) - varName = func->minimalSignature() + QString().number(arg_mod.index); + varName = func->minimalSignature() + QString::number(arg_mod.index); s << varName << "\", " << pyArgName << (refCount.action == ReferenceCount::Add ? ", true" : "") @@ -3531,7 +3634,7 @@ void CppGenerator::writeEnumConverterInitialization(QTextStream& s, const TypeEn const FlagsTypeEntry* flags = 0; if (enumType->isFlags()) - flags = reinterpret_cast<const FlagsTypeEntry*>(enumType); + flags = static_cast<const FlagsTypeEntry*>(enumType); s << INDENT << "// Register converter for " << enumFlagName << " '" << enumType->qualifiedCppName() << "'." << endl; s << INDENT << '{' << endl; @@ -3575,7 +3678,7 @@ void CppGenerator::writeEnumConverterInitialization(QTextStream& s, const TypeEn s << INDENT << '}' << endl; if (!flags) - writeEnumConverterInitialization(s, reinterpret_cast<const EnumTypeEntry*>(enumType)->flags()); + writeEnumConverterInitialization(s, static_cast<const EnumTypeEntry*>(enumType)->flags()); } void CppGenerator::writeContainerConverterInitialization(QTextStream& s, const AbstractMetaType* type) @@ -3656,10 +3759,7 @@ bool CppGenerator::supportsSequenceProtocol(const AbstractMetaClass* metaClass) } const ComplexTypeEntry* baseType = metaClass->typeEntry()->baseContainerType(); - if (baseType && baseType->isContainer()) - return true; - - return false; + return baseType && baseType->isContainer(); } bool CppGenerator::shouldGenerateGetSetList(const AbstractMetaClass* metaClass) @@ -3872,7 +3972,7 @@ void CppGenerator::writeMappingMethods(QTextStream &s, CodeSnipList snips = func->injectedCodeSnips(TypeSystem::CodeSnipPositionAny, TypeSystem::TargetLangCode); s << funcRetVal << ' ' << funcName << '(' << funcArgs << ')' << endl << '{' << endl; - writeInvalidPyObjectCheck(s, QLatin1String(PYTHON_SELF_VAR)); + writeInvalidPyObjectCheck(s, QLatin1String("self")); writeCppSelfDefinition(s, func, context); @@ -3899,7 +3999,7 @@ void CppGenerator::writeSequenceMethods(QTextStream &s, CodeSnipList snips = func->injectedCodeSnips(TypeSystem::CodeSnipPositionAny, TypeSystem::TargetLangCode); s << funcRetVal << ' ' << funcName << '(' << funcArgs << ')' << endl << '{' << endl; - writeInvalidPyObjectCheck(s, QLatin1String(PYTHON_SELF_VAR)); + writeInvalidPyObjectCheck(s, QLatin1String("self")); writeCppSelfDefinition(s, func, context); @@ -3964,7 +4064,6 @@ void CppGenerator::writeTypeAsMappingDefinition(QTextStream& s, const AbstractMe funcs.insert(QLatin1String("__msetitem__"), QString()); } - QString baseName = cpythonBaseName(metaClass); for (auto it = m_mpFuncs.cbegin(), end = m_mpFuncs.cend(); it != end; ++it) { const QString &mpName = it.key(); if (funcs[mpName].isEmpty()) @@ -4015,7 +4114,8 @@ void CppGenerator::writeTypeAsNumberDefinition(QTextStream& s, const AbstractMet QString baseName = cpythonBaseName(metaClass); - nb[QLatin1String("bool")] = hasBoolCast(metaClass) ? baseName + QLatin1String("___nb_bool") : QString(); + if (hasBoolCast(metaClass)) + nb.insert(QLatin1String("bool"), baseName + QLatin1String("___nb_bool")); for (QHash<QString, QString>::const_iterator it = m_nbFuncs.cbegin(), end = m_nbFuncs.cend(); it != end; ++it) { const QString &nbName = it.key(); @@ -4054,9 +4154,9 @@ void CppGenerator::writeTpTraverseFunction(QTextStream& s, const AbstractMetaCla { QString baseName = cpythonBaseName(metaClass); s << "static int "; - s << baseName << "_traverse(PyObject* " PYTHON_SELF_VAR ", visitproc visit, void* arg)" << endl; + s << baseName << "_traverse(PyObject* self, visitproc visit, void* arg)" << endl; s << '{' << endl; - s << INDENT << "return reinterpret_cast<PyTypeObject *>(SbkObject_TypeF())->tp_traverse(" PYTHON_SELF_VAR ", visit, arg);" << endl; + s << INDENT << "return reinterpret_cast<PyTypeObject *>(SbkObject_TypeF())->tp_traverse(self, visit, arg);" << endl; s << '}' << endl; } @@ -4064,9 +4164,9 @@ void CppGenerator::writeTpClearFunction(QTextStream& s, const AbstractMetaClass* { QString baseName = cpythonBaseName(metaClass); s << "static int "; - s << baseName << "_clear(PyObject* " PYTHON_SELF_VAR ")" << endl; + s << baseName << "_clear(PyObject* self)" << endl; s << '{' << endl; - s << INDENT << "return reinterpret_cast<PyTypeObject *>(SbkObject_TypeF())->tp_clear(" PYTHON_SELF_VAR ");" << endl; + s << INDENT << "return reinterpret_cast<PyTypeObject *>(SbkObject_TypeF())->tp_clear(self);" << endl; s << '}' << endl; } @@ -4074,7 +4174,7 @@ void CppGenerator::writeCopyFunction(QTextStream &s, GeneratorContext &context) { const AbstractMetaClass *metaClass = context.metaClass(); const QString className = chopType(cpythonTypeName(metaClass)); - s << "static PyObject* " << className << "___copy__(PyObject* " PYTHON_SELF_VAR ")" << endl; + s << "static PyObject* " << className << "___copy__(PyObject* self)" << endl; s << "{" << endl; writeCppSelfDefinition(s, context, false, true); QString conversionCode; @@ -4084,9 +4184,9 @@ void CppGenerator::writeCopyFunction(QTextStream &s, GeneratorContext &context) conversionCode = cpythonToPythonConversionFunction(context.preciseType()); s << INDENT << "PyObject* " << PYTHON_RETURN_VAR << " = " << conversionCode; - s << CPP_SELF_VAR ");" << endl; + s << CPP_SELF_VAR << ");" << endl; writeFunctionReturnErrorCheckSection(s); - s << INDENT << "return " PYTHON_RETURN_VAR ";" << endl; + s << INDENT << "return " << PYTHON_RETURN_VAR << ";" << endl; s << "}" << endl; s << endl; } @@ -4096,7 +4196,7 @@ void CppGenerator::writeGetterFunction(QTextStream &s, GeneratorContext &context) { ErrorCode errorCode(0); - s << "static PyObject* " << cpythonGetterFunctionName(metaField) << "(PyObject* " PYTHON_SELF_VAR ", void*)" << endl; + s << "static PyObject* " << cpythonGetterFunctionName(metaField) << "(PyObject* self, void*)" << endl; s << '{' << endl; writeCppSelfDefinition(s, context); @@ -4161,7 +4261,7 @@ void CppGenerator::writeGetterFunction(QTextStream &s, s << INDENT << "pyOut = "; s << "Shiboken::Object::newObject(reinterpret_cast<SbkObjectType *>(" << cpythonTypeNameExt(fieldType) << "), " << cppField << ", false, true);" << endl; - s << INDENT << "Shiboken::Object::setParent(" PYTHON_SELF_VAR ", pyOut)"; + s << INDENT << "Shiboken::Object::setParent(self, pyOut)"; } else { s << INDENT << "pyOut = "; writeToPythonConversion(s, fieldType, metaField->enclosingClass(), cppField); @@ -4177,7 +4277,7 @@ void CppGenerator::writeSetterFunction(QTextStream &s, GeneratorContext &context) { ErrorCode errorCode(0); - s << "static int " << cpythonSetterFunctionName(metaField) << "(PyObject* " PYTHON_SELF_VAR ", PyObject* pyIn, void*)" << endl; + s << "static int " << cpythonSetterFunctionName(metaField) << "(PyObject* self, PyObject* pyIn, void*)" << endl; s << '{' << endl; writeCppSelfDefinition(s, context); @@ -4193,7 +4293,7 @@ void CppGenerator::writeSetterFunction(QTextStream &s, AbstractMetaType* fieldType = metaField->type(); - s << INDENT << "PythonToCppFunc " << PYTHON_TO_CPP_VAR << ';' << endl; + s << INDENT << "PythonToCppFunc " << PYTHON_TO_CPP_VAR << "{nullptr};" << endl; s << INDENT << "if (!"; writeTypeCheck(s, fieldType, QLatin1String("pyIn"), isNumber(fieldType->typeEntry())); s << ") {" << endl; @@ -4219,6 +4319,8 @@ void CppGenerator::writeSetterFunction(QTextStream &s, s << INDENT << PYTHON_TO_CPP_VAR << "(pyIn, &cppOut_local);" << endl; s << INDENT << cppField << " = cppOut_local"; } else { + if (isPointerToConst(fieldType)) + s << "const "; s << getFullTypeNameWithoutModifiers(fieldType); s << QString::fromLatin1("*").repeated(fieldType->indirections()) << "& cppOut_ptr = "; s << cppField << ';' << endl; @@ -4227,7 +4329,7 @@ void CppGenerator::writeSetterFunction(QTextStream &s, s << ';' << endl << endl; if (isPointerToWrapperType(fieldType)) { - s << INDENT << "Shiboken::Object::keepReference(reinterpret_cast<SbkObject*>(" PYTHON_SELF_VAR "), \""; + s << INDENT << "Shiboken::Object::keepReference(reinterpret_cast<SbkObject*>(self), \""; s << metaField->name() << "\", pyIn);" << endl; } @@ -4240,12 +4342,12 @@ void CppGenerator::writeRichCompareFunction(QTextStream &s, GeneratorContext &co const AbstractMetaClass *metaClass = context.metaClass(); QString baseName = cpythonBaseName(metaClass); s << "static PyObject* "; - s << baseName << "_richcompare(PyObject* " PYTHON_SELF_VAR ", PyObject* " PYTHON_ARG ", int op)" << endl; + s << baseName << "_richcompare(PyObject* self, PyObject* " << PYTHON_ARG << ", int op)" << endl; s << '{' << endl; writeCppSelfDefinition(s, context, false, true); writeUnusedVariableCast(s, QLatin1String(CPP_SELF_VAR)); - s << INDENT << "PyObject* " PYTHON_RETURN_VAR " = 0;" << endl; - s << INDENT << "PythonToCppFunc " PYTHON_TO_CPP_VAR << ';' << endl; + s << INDENT << "PyObject* " << PYTHON_RETURN_VAR << " = 0;" << endl; + s << INDENT << "PythonToCppFunc " << PYTHON_TO_CPP_VAR << ';' << endl; writeUnusedVariableCast(s, QLatin1String(PYTHON_TO_CPP_VAR)); s << endl; @@ -4302,15 +4404,17 @@ void CppGenerator::writeRichCompareFunction(QTextStream &s, GeneratorContext &co CodeSnipList snips = func->injectedCodeSnips(); writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionAny, TypeSystem::TargetLangCode, func, func->arguments().constLast()); } else { - QString expression = QString::fromLatin1("%1%2 %3 (%4" CPP_ARG0 ")") - .arg(func->isPointerOperator() ? QLatin1String("&") : QString(), - QLatin1String(CPP_SELF_VAR), op, - shouldDereferenceAbstractMetaTypePointer(argType) ? QLatin1String("*") : QString()); s << INDENT; if (func->type()) - s << func->type()->cppSignature() << " " CPP_RETURN_VAR " = "; - s << expression << ';' << endl; - s << INDENT << PYTHON_RETURN_VAR " = "; + s << func->type()->cppSignature() << " " << CPP_RETURN_VAR << " = "; + // expression + if (func->isPointerOperator()) + s << '&'; + s << CPP_SELF_VAR << ' ' << op << '('; + if (shouldDereferenceAbstractMetaTypePointer(argType)) + s << '*'; + s << CPP_ARG0 << ");" << endl; + s << INDENT << PYTHON_RETURN_VAR << " = "; if (func->type()) writeToPythonConversion(s, func->type(), metaClass, QLatin1String(CPP_RETURN_VAR)); else @@ -4324,9 +4428,9 @@ void CppGenerator::writeRichCompareFunction(QTextStream &s, GeneratorContext &co s << " else {" << endl; if (operatorId == QLatin1String("Py_EQ") || operatorId == QLatin1String("Py_NE")) { Indentation indent(INDENT); - s << INDENT << PYTHON_RETURN_VAR " = " + s << INDENT << PYTHON_RETURN_VAR << " = " << (operatorId == QLatin1String("Py_EQ") ? "Py_False" : "Py_True") << ';' << endl; - s << INDENT << "Py_INCREF(" PYTHON_RETURN_VAR ");" << endl; + s << INDENT << "Py_INCREF(" << PYTHON_RETURN_VAR << ");" << endl; } else { Indentation indent(INDENT); s << INDENT << "goto " << baseName << "_RichComparison_TypeError;" << endl; @@ -4343,18 +4447,18 @@ void CppGenerator::writeRichCompareFunction(QTextStream &s, GeneratorContext &co } s << INDENT << '}' << endl << endl; - s << INDENT << "if (" PYTHON_RETURN_VAR " && !PyErr_Occurred())" << endl; + s << INDENT << "if (" << PYTHON_RETURN_VAR << " && !PyErr_Occurred())" << endl; { Indentation indent(INDENT); - s << INDENT << "return " PYTHON_RETURN_VAR ";" << endl; + s << INDENT << "return " << PYTHON_RETURN_VAR << ";" << endl; } s << INDENT << baseName << "_RichComparison_TypeError:" << endl; s << INDENT << "PyErr_SetString(PyExc_NotImplementedError, \"operator not implemented.\");" << endl; - s << INDENT << "return " << m_currentErrorCode << ';' << endl << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl << endl; s << '}' << endl << endl; } -void CppGenerator::writeMethodDefinitionEntry(QTextStream& s, const AbstractMetaFunctionList overloads) +void CppGenerator::writeMethodDefinitionEntry(QTextStream& s, const AbstractMetaFunctionList &overloads) { Q_ASSERT(!overloads.isEmpty()); OverloadData overloadData(overloads, this); @@ -4378,7 +4482,7 @@ void CppGenerator::writeMethodDefinitionEntry(QTextStream& s, const AbstractMeta s << "|METH_STATIC"; } -void CppGenerator::writeMethodDefinition(QTextStream& s, const AbstractMetaFunctionList overloads) +void CppGenerator::writeMethodDefinition(QTextStream& s, const AbstractMetaFunctionList &overloads) { Q_ASSERT(!overloads.isEmpty()); const AbstractMetaFunction* func = overloads.constFirst(); @@ -4510,7 +4614,7 @@ void CppGenerator::writeEnumInitialization(QTextStream& s, const AbstractMetaEnu s << INDENT << "if (!" << cpythonTypeNameExt(cppEnum->typeEntry()) << ')' << endl; { Indentation indent(INDENT); - s << INDENT << "return " << m_currentErrorCode << ';' << endl << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl << endl; } } @@ -4543,7 +4647,7 @@ void CppGenerator::writeEnumInitialization(QTextStream& s, const AbstractMetaEnu << "))->tp_dict, \"" << enumValue->name() << "\", anonEnumItem) < 0)" << endl; { Indentation indent(INDENT); - s << INDENT << "return " << m_currentErrorCode << ';' << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl; } s << INDENT << "Py_DECREF(anonEnumItem);" << endl; } @@ -4553,7 +4657,7 @@ void CppGenerator::writeEnumInitialization(QTextStream& s, const AbstractMetaEnu s << enumValueText << ") < 0)" << endl; { Indentation indent(INDENT); - s << INDENT << "return " << m_currentErrorCode << ';' << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl; } } break; @@ -4564,7 +4668,7 @@ void CppGenerator::writeEnumInitialization(QTextStream& s, const AbstractMetaEnu Indentation indent(INDENT); s << INDENT << enclosingObjectVariable << ", \"" << enumValue->name() << "\", "; s << enumValueText << "))" << endl; - s << INDENT << "return " << m_currentErrorCode << ';' << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl; } break; case EnumClass: { @@ -4573,7 +4677,7 @@ void CppGenerator::writeEnumInitialization(QTextStream& s, const AbstractMetaEnu Indentation indent(INDENT); s << INDENT << enumVarTypeObj<< ", \"" << enumValue->name() << "\", " << enumValueText << "))" << endl - << INDENT << "return " << m_currentErrorCode << ';' << endl; + << INDENT << returnStatement(m_currentErrorCode) << endl; } break; } @@ -4618,11 +4722,11 @@ void CppGenerator::writeFlagsToLong(QTextStream& s, const AbstractMetaEnum* cppE FlagsTypeEntry* flagsEntry = cppEnum->typeEntry()->flags(); if (!flagsEntry) return; - s << "static PyObject* " << cpythonEnumName(cppEnum) << "_long(PyObject* " PYTHON_SELF_VAR ")" << endl; + s << "static PyObject* " << cpythonEnumName(cppEnum) << "_long(PyObject* self)" << endl; s << "{" << endl; s << INDENT << "int val;" << endl; AbstractMetaType* flagsType = buildAbstractMetaTypeFromTypeEntry(flagsEntry); - s << INDENT << cpythonToCppConversionFunction(flagsType) << PYTHON_SELF_VAR << ", &val);" << endl; + s << INDENT << cpythonToCppConversionFunction(flagsType) << "self, &val);" << endl; s << INDENT << "return Shiboken::Conversions::copyToPython(Shiboken::Conversions::PrimitiveTypeConverter<int>(), &val);" << endl; s << "}" << endl; } @@ -4632,12 +4736,12 @@ void CppGenerator::writeFlagsNonZero(QTextStream& s, const AbstractMetaEnum* cpp FlagsTypeEntry* flagsEntry = cppEnum->typeEntry()->flags(); if (!flagsEntry) return; - s << "static int " << cpythonEnumName(cppEnum) << "__nonzero(PyObject* " PYTHON_SELF_VAR ")" << endl; + s << "static int " << cpythonEnumName(cppEnum) << "__nonzero(PyObject* self)" << endl; s << "{" << endl; s << INDENT << "int val;" << endl; AbstractMetaType* flagsType = buildAbstractMetaTypeFromTypeEntry(flagsEntry); - s << INDENT << cpythonToCppConversionFunction(flagsType) << PYTHON_SELF_VAR << ", &val);" << endl; + s << INDENT << cpythonToCppConversionFunction(flagsType) << "self, &val);" << endl; s << INDENT << "return val != 0;" << endl; s << "}" << endl; } @@ -4679,24 +4783,24 @@ void CppGenerator::writeFlagsNumberMethodsDefinition(QTextStream& s, const Abstr } void CppGenerator::writeFlagsBinaryOperator(QTextStream& s, const AbstractMetaEnum* cppEnum, - QString pyOpName, QString cppOpName) + const QString &pyOpName, const QString &cppOpName) { FlagsTypeEntry* flagsEntry = cppEnum->typeEntry()->flags(); Q_ASSERT(flagsEntry); - s << "PyObject* " << cpythonEnumName(cppEnum) << "___" << pyOpName << "__(PyObject* " PYTHON_SELF_VAR ", PyObject* " PYTHON_ARG ")" << endl; + s << "PyObject* " << cpythonEnumName(cppEnum) << "___" << pyOpName << "__(PyObject* self, PyObject* " << PYTHON_ARG << ")" << endl; s << '{' << endl; AbstractMetaType* flagsType = buildAbstractMetaTypeFromTypeEntry(flagsEntry); - s << INDENT << "::" << flagsEntry->originalName() << " cppResult, " CPP_SELF_VAR ", cppArg;" << endl; + s << INDENT << "::" << flagsEntry->originalName() << " cppResult, " << CPP_SELF_VAR << ", cppArg;" << endl; s << "#ifdef IS_PY3K" << endl; - s << INDENT << CPP_SELF_VAR " = (::" << flagsEntry->originalName() << ")(int)PyLong_AsLong(" PYTHON_SELF_VAR ");" << endl; - s << INDENT << "cppArg = (" << flagsEntry->originalName() << ")(int)PyLong_AsLong(" PYTHON_ARG ");" << endl; + s << INDENT << CPP_SELF_VAR << " = (::" << flagsEntry->originalName() << ")(int)PyLong_AsLong(self);" << endl; + s << INDENT << "cppArg = (" << flagsEntry->originalName() << ")(int)PyLong_AsLong(" << PYTHON_ARG << ");" << endl; s << "#else" << endl; - s << INDENT << CPP_SELF_VAR " = (::" << flagsEntry->originalName() << ")(int)PyInt_AsLong(" PYTHON_SELF_VAR ");" << endl; - s << INDENT << "cppArg = (" << flagsEntry->originalName() << ")(int)PyInt_AsLong(" PYTHON_ARG ");" << endl; + s << INDENT << CPP_SELF_VAR << " = (::" << flagsEntry->originalName() << ")(int)PyInt_AsLong(self);" << endl; + s << INDENT << "cppArg = (" << flagsEntry->originalName() << ")(int)PyInt_AsLong(" << PYTHON_ARG << ");" << endl; s << "#endif" << endl << endl; - s << INDENT << "cppResult = " CPP_SELF_VAR " " << cppOpName << " cppArg;" << endl; + s << INDENT << "cppResult = " << CPP_SELF_VAR << " " << cppOpName << " cppArg;" << endl; s << INDENT << "return "; writeToPythonConversion(s, flagsType, 0, QLatin1String("cppResult")); s << ';' << endl; @@ -4704,23 +4808,24 @@ void CppGenerator::writeFlagsBinaryOperator(QTextStream& s, const AbstractMetaEn } void CppGenerator::writeFlagsUnaryOperator(QTextStream& s, const AbstractMetaEnum* cppEnum, - QString pyOpName, QString cppOpName, bool boolResult) + const QString &pyOpName, + const QString &cppOpName, bool boolResult) { FlagsTypeEntry* flagsEntry = cppEnum->typeEntry()->flags(); Q_ASSERT(flagsEntry); - s << "PyObject* " << cpythonEnumName(cppEnum) << "___" << pyOpName << "__(PyObject* " PYTHON_SELF_VAR ", PyObject* " PYTHON_ARG ")" << endl; + s << "PyObject* " << cpythonEnumName(cppEnum) << "___" << pyOpName << "__(PyObject* self, PyObject* " << PYTHON_ARG << ")" << endl; s << '{' << endl; AbstractMetaType* flagsType = buildAbstractMetaTypeFromTypeEntry(flagsEntry); - s << INDENT << "::" << flagsEntry->originalName() << " " CPP_SELF_VAR ";" << endl; - s << INDENT << cpythonToCppConversionFunction(flagsType) << PYTHON_SELF_VAR << ", &" CPP_SELF_VAR ");" << endl; + s << INDENT << "::" << flagsEntry->originalName() << " " << CPP_SELF_VAR << ";" << endl; + s << INDENT << cpythonToCppConversionFunction(flagsType) << "self, &" << CPP_SELF_VAR << ");" << endl; s << INDENT; if (boolResult) s << "bool"; else s << "::" << flagsEntry->originalName(); - s << " cppResult = " << cppOpName << CPP_SELF_VAR ";" << endl; + s << " cppResult = " << cppOpName << CPP_SELF_VAR << ';' << endl; s << INDENT << "return "; if (boolResult) s << "PyBool_FromLong(cppResult)"; @@ -4846,8 +4951,16 @@ void CppGenerator::writeClassRegister(QTextStream &s, else s << INDENT << "0," << endl; - // 9:isInnerClass - s << INDENT << (hasEnclosingClass ? "true" : "false") << endl; + // 9:wrapperflags + QByteArrayList wrapperFlags; + if (hasEnclosingClass) + wrapperFlags.append(QByteArrayLiteral("Shiboken::ObjectType::WrapperFlags::InnerClass")); + if (metaClass->deleteInMainThread()) + wrapperFlags.append(QByteArrayLiteral("Shiboken::ObjectType::WrapperFlags::DeleteInMainThread")); + if (wrapperFlags.isEmpty()) + s << INDENT << '0'; + else + s << INDENT << wrapperFlags.join(" | "); } s << INDENT << ");" << endl; s << INDENT << endl; @@ -5042,25 +5155,27 @@ void CppGenerator::writeTypeDiscoveryFunction(QTextStream& s, const AbstractMeta s << "}\n\n"; } -QString CppGenerator::writeSmartPointerGetterCast() { - return QLatin1String("const_cast<char *>(" SMART_POINTER_GETTER ")"); +QString CppGenerator::writeSmartPointerGetterCast() +{ + return QLatin1String("const_cast<char *>(") + + QLatin1String(SMART_POINTER_GETTER) + QLatin1Char(')'); } void CppGenerator::writeSetattroFunction(QTextStream &s, GeneratorContext &context) { const AbstractMetaClass* metaClass = context.metaClass(); - s << "static int " << cpythonSetattroFunctionName(metaClass) << "(PyObject* " PYTHON_SELF_VAR ", PyObject* name, PyObject* value)" << endl; + s << "static int " << cpythonSetattroFunctionName(metaClass) << "(PyObject* self, PyObject* name, PyObject* value)" << endl; s << '{' << endl; if (usePySideExtensions()) { - s << INDENT << "Shiboken::AutoDecRef pp(reinterpret_cast<PyObject*>(PySide::Property::getObject(" PYTHON_SELF_VAR ", name)));" << endl; + s << INDENT << "Shiboken::AutoDecRef pp(reinterpret_cast<PyObject*>(PySide::Property::getObject(self, name)));" << endl; s << INDENT << "if (!pp.isNull())" << endl; Indentation indent(INDENT); - s << INDENT << "return PySide::Property::setValue(reinterpret_cast<PySideProperty*>(pp.object()), " PYTHON_SELF_VAR ", value);" << endl; + s << INDENT << "return PySide::Property::setValue(reinterpret_cast<PySideProperty*>(pp.object()), self, value);" << endl; } if (context.forSmartPointer()) { s << INDENT << "// Try to find the 'name' attribute, by retrieving the PyObject for the corresponding C++ object held by the smart pointer." << endl; - s << INDENT << "PyObject *rawObj = PyObject_CallMethod(" PYTHON_SELF_VAR ", " + s << INDENT << "PyObject *rawObj = PyObject_CallMethod(self, " << writeSmartPointerGetterCast() << ", 0);" << endl; s << INDENT << "if (rawObj) {" << endl; { @@ -5078,7 +5193,7 @@ void CppGenerator::writeSetattroFunction(QTextStream &s, GeneratorContext &conte } - s << INDENT << "return PyObject_GenericSetAttr(" PYTHON_SELF_VAR ", name, value);" << endl; + s << INDENT << "return PyObject_GenericSetAttr(self, name, value);" << endl; s << '}' << endl; } @@ -5088,27 +5203,29 @@ static inline QString qMetaObjectClassName() { return QStringLiteral("QMetaObjec void CppGenerator::writeGetattroFunction(QTextStream& s, GeneratorContext &context) { const AbstractMetaClass* metaClass = context.metaClass(); - s << "static PyObject* " << cpythonGetattroFunctionName(metaClass) << "(PyObject* " PYTHON_SELF_VAR ", PyObject* name)" << endl; + s << "static PyObject* " << cpythonGetattroFunctionName(metaClass) << "(PyObject* self, PyObject* name)" << endl; s << '{' << endl; QString getattrFunc; if (usePySideExtensions() && metaClass->isQObject()) { AbstractMetaClass *qobjectClass = AbstractMetaClass::findClass(classes(), qObjectClassName()); - getattrFunc = QString::fromLatin1("PySide::getMetaDataFromQObject(%1, " PYTHON_SELF_VAR ", name)") - .arg(cpythonWrapperCPtr(qobjectClass, QLatin1String(PYTHON_SELF_VAR))); + QTextStream(&getattrFunc) << "PySide::getMetaDataFromQObject(" + << cpythonWrapperCPtr(qobjectClass, QLatin1String("self")) + << ", self, name)"; } else { - getattrFunc = QLatin1String("PyObject_GenericGetAttr(" PYTHON_SELF_VAR ", name)"); + getattrFunc = QLatin1String("PyObject_GenericGetAttr(") + QLatin1String("self") + + QLatin1String(", name)"); } if (classNeedsGetattroFunction(metaClass)) { - s << INDENT << "if (" PYTHON_SELF_VAR ") {" << endl; + s << INDENT << "if (self) {" << endl; { Indentation indent(INDENT); s << INDENT << "// Search the method in the instance dict" << endl; - s << INDENT << "if (reinterpret_cast<SbkObject*>(" PYTHON_SELF_VAR ")->ob_dict) {" << endl; + s << INDENT << "if (reinterpret_cast<SbkObject*>(self)->ob_dict) {" << endl; { Indentation indent(INDENT); - s << INDENT << "PyObject* meth = PyDict_GetItem(reinterpret_cast<SbkObject*>(" PYTHON_SELF_VAR ")->ob_dict, name);" << endl; + s << INDENT << "PyObject* meth = PyDict_GetItem(reinterpret_cast<SbkObject*>(self)->ob_dict, name);" << endl; s << INDENT << "if (meth) {" << endl; { Indentation indent(INDENT); @@ -5119,16 +5236,16 @@ void CppGenerator::writeGetattroFunction(QTextStream& s, GeneratorContext &conte } s << INDENT << '}' << endl; s << INDENT << "// Search the method in the type dict" << endl; - s << INDENT << "if (Shiboken::Object::isUserType(" PYTHON_SELF_VAR ")) {" << endl; + s << INDENT << "if (Shiboken::Object::isUserType(self)) {" << endl; { Indentation indent(INDENT); // PYSIDE-772: Perform optimized name mangling. - s << INDENT << "Shiboken::AutoDecRef tmp(_Pep_PrivateMangle(" PYTHON_SELF_VAR ", name));" << endl; - s << INDENT << "PyObject *meth = PyDict_GetItem(Py_TYPE(" PYTHON_SELF_VAR ")->tp_dict, tmp);" << endl; + s << INDENT << "Shiboken::AutoDecRef tmp(_Pep_PrivateMangle(self, name));" << endl; + s << INDENT << "PyObject *meth = PyDict_GetItem(Py_TYPE(self)->tp_dict, tmp);" << endl; s << INDENT << "if (meth)" << endl; { Indentation indent(INDENT); - s << INDENT << "return PyFunction_Check(meth) ? SBK_PyMethod_New(meth, " PYTHON_SELF_VAR ") : " << getattrFunc << ';' << endl; + s << INDENT << "return PyFunction_Check(meth) ? SBK_PyMethod_New(meth, self) : " << getattrFunc << ';' << endl; } } s << INDENT << '}' << endl; @@ -5147,7 +5264,7 @@ void CppGenerator::writeGetattroFunction(QTextStream& s, GeneratorContext &conte s << INDENT << "};" << endl; s << INDENT << "if (Shiboken::String::compare(name, \"" << func->name() << "\") == 0)" << endl; Indentation indent(INDENT); - s << INDENT << "return PyCFunction_NewEx(&non_static_" << defName << ", " PYTHON_SELF_VAR ", 0);" << endl; + s << INDENT << "return PyCFunction_NewEx(&non_static_" << defName << ", self, 0);" << endl; } } s << INDENT << '}' << endl; @@ -5168,7 +5285,7 @@ void CppGenerator::writeGetattroFunction(QTextStream& s, GeneratorContext &conte s << INDENT << "// Try to find the 'name' attribute, by retrieving the PyObject for " "the corresponding C++ object held by the smart pointer." << endl; - s << INDENT << "PyObject *rawObj = PyObject_CallMethod(" PYTHON_SELF_VAR ", " + s << INDENT << "PyObject *rawObj = PyObject_CallMethod(self, " << writeSmartPointerGetterCast() << ", 0);" << endl; s << INDENT << "if (rawObj) {" << endl; { @@ -5289,15 +5406,11 @@ bool CppGenerator::finishGeneration() QString moduleFileName(outputDirectory() + QLatin1Char('/') + subDirectoryForPackage(packageName())); moduleFileName += QLatin1Char('/') + moduleName().toLower() + QLatin1String("_module_wrapper.cpp"); - QFile file(moduleFileName); - verifyDirectoryFor(file); - if (!file.open(QFile::WriteOnly)) { - qCWarning(lcShiboken).noquote().nospace() - << "Error writing file: " << QDir::toNativeSeparators(moduleFileName); - return false; - } - QTextStream s(&file); + verifyDirectoryFor(moduleFileName); + FileOut file(moduleFileName); + + QTextStream &s = file.stream; // write license comment s << licenseComment() << endl; @@ -5305,10 +5418,10 @@ bool CppGenerator::finishGeneration() s << "#include <sbkpython.h>" << endl; s << "#include <shiboken.h>" << endl; s << "#include <algorithm>" << endl; + s << "#include <signature.h>" << endl; if (usePySideExtensions()) { s << includeQDebug; s << "#include <pyside.h>" << endl; - s << "#include <signature.h>" << endl; s << "#include <qapp_macro.h>" << endl; } @@ -5328,13 +5441,12 @@ bool CppGenerator::finishGeneration() } TypeDatabase* typeDb = TypeDatabase::instance(); - TypeSystemTypeEntry* moduleEntry = reinterpret_cast<TypeSystemTypeEntry*>(typeDb->findType(packageName())); + const TypeSystemTypeEntry *moduleEntry = typeDb->findTypeSystemType(packageName()); + Q_ASSERT(moduleEntry); //Extra includes s << endl << "// Extra includes" << endl; - QVector<Include> extraIncludes; - if (moduleEntry) - extraIncludes = moduleEntry->extraIncludes(); + QVector<Include> extraIncludes = moduleEntry->extraIncludes(); for (AbstractMetaEnum *cppEnum : qAsConst(globalEnums)) extraIncludes.append(cppEnum->typeEntry()->extraIncludes()); qSort(extraIncludes.begin(), extraIncludes.end()); @@ -5343,14 +5455,15 @@ bool CppGenerator::finishGeneration() s << endl; s << "// Current module's type array." << endl; - s << "PyTypeObject** " << cppApiVariableName() << ';' << endl; + s << "PyTypeObject** " << cppApiVariableName() << " = nullptr;" << endl; + + s << "// Current module's PyObject pointer." << endl; + s << "PyObject* " << pythonModuleObjectName() << " = nullptr;" << endl; s << "// Current module's converter array." << endl; - s << "SbkConverter** " << convertersVariableName() << ';' << endl; + s << "SbkConverter** " << convertersVariableName() << " = nullptr;" << endl; - CodeSnipList snips; - if (moduleEntry) - snips = moduleEntry->codeSnips(); + const CodeSnipList snips = moduleEntry->codeSnips(); // module inject-code native/beginning if (!snips.isEmpty()) { @@ -5519,6 +5632,9 @@ bool CppGenerator::finishGeneration() s << moduleName() << "_methods);" << endl; s << "#endif" << endl << endl; + s << INDENT << "// Make module available from global scope" << endl; + s << INDENT << pythonModuleObjectName() << " = module;" << endl << endl; + //s << INDENT << "// Initialize converters for primitive types." << endl; //s << INDENT << "initConverters();" << endl << endl; @@ -5601,25 +5717,28 @@ bool CppGenerator::finishGeneration() // cleanup staticMetaObject attribute s << INDENT << "PySide::registerCleanupFunction(cleanTypesAttributes);" << endl << endl; + } - // PYSIDE-510: Create a signatures string for the introspection feature. - s << "// The signatures string for the global functions." << endl; - s << "// Multiple signatures have their index \"n:\" in front." << endl; - s << "const char " << moduleName() << "_SignaturesString[] = \"\"" << endl; - QString line; - while (signatureStream.readLineInto(&line)) - s << INDENT << '"' << line << "\\n\"" << endl; - s << ';' << endl; - // finish the rest of __signature__ initialization. - s << INDENT << "FinishSignatureInitialization(module, " << moduleName() - << "_SignaturesString);" << endl; + // PYSIDE-510: Create a signatures string for the introspection feature. + s << "// The signatures string for the global functions." << endl; + s << "// Multiple signatures have their index \"n:\" in front." << endl; + s << "const char " << moduleName() << "_SignaturesString[] = \"\"" << endl; + QString line; + while (signatureStream.readLineInto(&line)) + s << INDENT << '"' << line << "\\n\"" << endl; + s << ';' << endl; + // finish the rest of __signature__ initialization. + s << INDENT << "FinishSignatureInitialization(module, " << moduleName() + << "_SignaturesString);" << endl; + + if (usePySideExtensions()) { // initialize the qApp module. - s << INDENT << "NotifyModuleForQApp(module);" << endl << endl; + s << INDENT << "NotifyModuleForQApp(module);" << endl; } - + s << endl; s << "SBK_MODULE_INIT_FUNCTION_END" << endl; - return true; + return file.done() != FileOut::Failure; } static ArgumentOwner getArgumentOwner(const AbstractMetaFunction* func, int argIndex) @@ -5663,22 +5782,20 @@ bool CppGenerator::writeParentChildManagement(QTextStream& s, const AbstractMeta if (parentIndex == 0) { parentVariable = QLatin1String(PYTHON_RETURN_VAR); } else if (parentIndex == -1) { - parentVariable = QLatin1String(PYTHON_SELF_VAR); + parentVariable = QLatin1String("self"); } else { parentVariable = usePyArgs - ? QString::fromLatin1(PYTHON_ARGS "[%1]").arg(parentIndex - 1) - : QLatin1String(PYTHON_ARG); + ? pythonArgsAt(parentIndex - 1) : QLatin1String(PYTHON_ARG); } } if (childIndex == 0) { childVariable = QLatin1String(PYTHON_RETURN_VAR); } else if (childIndex == -1) { - childVariable = QLatin1String(PYTHON_SELF_VAR); + childVariable = QLatin1String("self"); } else { childVariable = usePyArgs - ? QString::fromLatin1(PYTHON_ARGS "[%1]").arg(childIndex - 1) - : QLatin1String(PYTHON_ARG); + ? pythonArgsAt(childIndex - 1) : QLatin1String(PYTHON_ARG); } s << INDENT << "Shiboken::Object::setParent(" << parentVariable << ", " << childVariable << ");\n"; @@ -5717,7 +5834,7 @@ void CppGenerator::writeReturnValueHeuristics(QTextStream& s, const AbstractMeta ArgumentOwner argOwner = getArgumentOwner(func, ArgumentOwner::ReturnIndex); if (argOwner.action == ArgumentOwner::Invalid || argOwner.index != ArgumentOwner::ThisIndex) { if (isPointerToWrapperType(type)) - s << INDENT << "Shiboken::Object::setParent(" << self << ", " PYTHON_RETURN_VAR ");" << endl; + s << INDENT << "Shiboken::Object::setParent(self, " << PYTHON_RETURN_VAR << ");" << endl; } } @@ -5737,19 +5854,19 @@ void CppGenerator::writeStdListWrapperMethods(QTextStream &s, GeneratorContext & ErrorCode errorCode(0); // __len__ - s << "Py_ssize_t " << cpythonBaseName(metaClass->typeEntry()) << "__len__(PyObject* " PYTHON_SELF_VAR ")" << endl; + s << "Py_ssize_t " << cpythonBaseName(metaClass->typeEntry()) << "__len__(PyObject* self)" << endl; s << '{' << endl; writeCppSelfDefinition(s, context); - s << INDENT << "return " CPP_SELF_VAR "->size();" << endl; + s << INDENT << "return " << CPP_SELF_VAR << "->size();" << endl; s << '}' << endl; // __getitem__ - s << "PyObject* " << cpythonBaseName(metaClass->typeEntry()) << "__getitem__(PyObject* " PYTHON_SELF_VAR ", Py_ssize_t _i)" << endl; + s << "PyObject* " << cpythonBaseName(metaClass->typeEntry()) << "__getitem__(PyObject* self, Py_ssize_t _i)" << endl; s << '{' << endl; writeCppSelfDefinition(s, context); writeIndexError(s, QLatin1String("index out of bounds")); - s << INDENT << metaClass->qualifiedCppName() << "::iterator _item = " CPP_SELF_VAR "->begin();" << endl; + s << INDENT << metaClass->qualifiedCppName() << "::iterator _item = " << CPP_SELF_VAR << "->begin();" << endl; s << INDENT << "for (Py_ssize_t pos = 0; pos < _i; pos++) _item++;" << endl; const AbstractMetaType* itemType = metaClass->templateBaseClassInstantiations().constFirst(); @@ -5761,7 +5878,7 @@ void CppGenerator::writeStdListWrapperMethods(QTextStream &s, GeneratorContext & // __setitem__ ErrorCode errorCode2(-1); - s << "int " << cpythonBaseName(metaClass->typeEntry()) << "__setitem__(PyObject* " PYTHON_SELF_VAR ", Py_ssize_t _i, PyObject* pyArg)" << endl; + s << "int " << cpythonBaseName(metaClass->typeEntry()) << "__setitem__(PyObject* self, Py_ssize_t _i, PyObject* pyArg)" << endl; s << '{' << endl; writeCppSelfDefinition(s, context); writeIndexError(s, QLatin1String("list assignment index out of range")); @@ -5779,7 +5896,7 @@ void CppGenerator::writeStdListWrapperMethods(QTextStream &s, GeneratorContext & s << INDENT << '}' << endl; writeArgumentConversion(s, itemType, QLatin1String("cppValue"), QLatin1String("pyArg"), metaClass); - s << INDENT << metaClass->qualifiedCppName() << "::iterator _item = " CPP_SELF_VAR "->begin();" << endl; + s << INDENT << metaClass->qualifiedCppName() << "::iterator _item = " << CPP_SELF_VAR << "->begin();" << endl; s << INDENT << "for (Py_ssize_t pos = 0; pos < _i; pos++) _item++;" << endl; s << INDENT << "*_item = cppValue;" << endl; s << INDENT << "return 0;" << endl; @@ -5787,11 +5904,11 @@ void CppGenerator::writeStdListWrapperMethods(QTextStream &s, GeneratorContext & } void CppGenerator::writeIndexError(QTextStream& s, const QString& errorMsg) { - s << INDENT << "if (_i < 0 || _i >= (Py_ssize_t) " CPP_SELF_VAR "->size()) {" << endl; + s << INDENT << "if (_i < 0 || _i >= (Py_ssize_t) " << CPP_SELF_VAR << "->size()) {" << endl; { Indentation indent(INDENT); s << INDENT << "PyErr_SetString(PyExc_IndexError, \"" << errorMsg << "\");" << endl; - s << INDENT << "return " << m_currentErrorCode << ';' << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl; } s << INDENT << '}' << endl; } @@ -5808,7 +5925,10 @@ QString CppGenerator::writeReprFunction(QTextStream &s, GeneratorContext &contex s << INDENT << "QBuffer buffer;" << endl; s << INDENT << "buffer.open(QBuffer::ReadWrite);" << endl; s << INDENT << "QDebug dbg(&buffer);" << endl; - s << INDENT << "dbg << " << (metaClass->typeEntry()->isValue() ? "*" : "") << CPP_SELF_VAR ";" << endl; + s << INDENT << "dbg << "; + if (metaClass->typeEntry()->isValue()) + s << '*'; + s << CPP_SELF_VAR << ';' << endl; s << INDENT << "buffer.close();" << endl; s << INDENT << "QByteArray str = buffer.data();" << endl; s << INDENT << "int idx = str.indexOf('(');" << endl; diff --git a/sources/shiboken2/generator/shiboken2/cppgenerator.h b/sources/shiboken2/generator/shiboken2/cppgenerator.h index 1b59bcda7..55a1c265d 100644 --- a/sources/shiboken2/generator/shiboken2/cppgenerator.h +++ b/sources/shiboken2/generator/shiboken2/cppgenerator.h @@ -234,8 +234,8 @@ private: void writeClassDefinition(QTextStream &s, const AbstractMetaClass *metaClass, GeneratorContext &classContext); - void writeMethodDefinitionEntry(QTextStream& s, const AbstractMetaFunctionList overloads); - void writeMethodDefinition(QTextStream& s, const AbstractMetaFunctionList overloads); + void writeMethodDefinitionEntry(QTextStream& s, const AbstractMetaFunctionList &overloads); + void writeMethodDefinition(QTextStream& s, const AbstractMetaFunctionList &overloads); void writeSignatureInfo(QTextStream &s, const AbstractMetaFunctionList &overloads); /// Writes the implementation of all methods part of python sequence protocol void writeSequenceMethods(QTextStream &s, @@ -275,9 +275,10 @@ private: void writeFlagsNonZero(QTextStream& s, const AbstractMetaEnum* cppEnum); void writeFlagsNumberMethodsDefinition(QTextStream& s, const AbstractMetaEnum* cppEnum); void writeFlagsBinaryOperator(QTextStream& s, const AbstractMetaEnum* cppEnum, - QString pyOpName, QString cppOpName); + const QString &pyOpName, const QString &cppOpName); void writeFlagsUnaryOperator(QTextStream& s, const AbstractMetaEnum* cppEnum, - QString pyOpName, QString cppOpName, bool boolResult = false); + const QString &pyOpName, const QString &cppOpName, + bool boolResult = false); /// Writes the function that registers the multiple inheritance information for the classes that need it. void writeMultipleInheritanceInitializerFunction(QTextStream& s, const AbstractMetaClass* metaClass); @@ -292,7 +293,7 @@ private: void writeParentChildManagement(QTextStream& s, const AbstractMetaFunction* func, bool userHeuristicForReturn); bool writeParentChildManagement(QTextStream& s, const AbstractMetaFunction* func, int argIndex, bool userHeuristicPolicy); - void writeReturnValueHeuristics(QTextStream& s, const AbstractMetaFunction* func, const QString& self = QLatin1String(PYTHON_SELF_VAR)); + void writeReturnValueHeuristics(QTextStream& s, const AbstractMetaFunction* func, const QString& self = QLatin1String("self")); void writeInitQtMetaTypeFunctionBody(QTextStream &s, GeneratorContext &context) const; /** @@ -327,7 +328,9 @@ private: QString writeReprFunction(QTextStream &s, GeneratorContext &context); - bool hasBoolCast(const AbstractMetaClass* metaClass) const; + const AbstractMetaFunction *boolCast(const AbstractMetaClass* metaClass) const; + bool hasBoolCast(const AbstractMetaClass* metaClass) const + { return boolCast(metaClass) != nullptr; } // Number protocol structure members names. static QHash<QString, QString> m_nbFuncs; diff --git a/sources/shiboken2/generator/shiboken2/headergenerator.cpp b/sources/shiboken2/generator/shiboken2/headergenerator.cpp index 8fde3cd31..8881d71f4 100644 --- a/sources/shiboken2/generator/shiboken2/headergenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/headergenerator.cpp @@ -32,6 +32,8 @@ #include <reporthandler.h> #include <fileout.h> +#include <algorithm> + #include <QtCore/QDir> #include <QtCore/QTextStream> #include <QtCore/QVariant> @@ -49,11 +51,10 @@ QString HeaderGenerator::fileNameForContext(GeneratorContext &context) const QString fileNameBase = metaClass->qualifiedCppName().toLower(); fileNameBase.replace(QLatin1String("::"), QLatin1String("_")); return fileNameBase + fileNameSuffix(); - } else { - const AbstractMetaType *smartPointerType = context.preciseType(); - QString fileNameBase = getFileNameBaseForSmartPointer(smartPointerType, metaClass); - return fileNameBase + fileNameSuffix(); } + const AbstractMetaType *smartPointerType = context.preciseType(); + QString fileNameBase = getFileNameBaseForSmartPointer(smartPointerType, metaClass); + return fileNameBase + fileNameSuffix(); } void HeaderGenerator::writeCopyCtor(QTextStream& s, const AbstractMetaClass* metaClass) const @@ -116,8 +117,6 @@ void HeaderGenerator::generateClass(QTextStream &s, GeneratorContext &classConte if (!avoidProtectedHack()) s << "#define protected public" << endl << endl; - s << "#include <shiboken.h>" << endl << endl; - //Includes s << metaClass->typeEntry()->include() << endl; @@ -173,7 +172,7 @@ void HeaderGenerator::generateClass(QTextStream &s, GeneratorContext &classConte s << INDENT << "void* qt_metacast(const char* _clname) override;" << endl; } - if (m_inheritedOverloads.size()) { + if (!m_inheritedOverloads.isEmpty()) { s << INDENT << "// Inherited overloads, because the using keyword sux" << endl; writeInheritedOverloads(s); m_inheritedOverloads.clear(); @@ -230,7 +229,7 @@ void HeaderGenerator::writeFunction(QTextStream& s, const AbstractMetaFunction* QString argName = arg->name(); const TypeEntry* enumTypeEntry = 0; if (arg->type()->isFlags()) - enumTypeEntry = reinterpret_cast<const FlagsTypeEntry*>(arg->type()->typeEntry())->originator(); + enumTypeEntry = static_cast<const FlagsTypeEntry*>(arg->type()->typeEntry())->originator(); else if (arg->type()->isEnum()) enumTypeEntry = arg->type()->typeEntry(); if (enumTypeEntry) @@ -284,49 +283,86 @@ void HeaderGenerator::writeFunction(QTextStream& s, const AbstractMetaFunction* } } -static void _writeTypeIndexDefineLine(QTextStream& s, const QString& variableName, int typeIndex) +static void _writeTypeIndexValue(QTextStream& s, const QString& variableName, + int typeIndex) { - s << "#define "; - s.setFieldWidth(60); + s << " "; + s.setFieldWidth(56); s << variableName; s.setFieldWidth(0); - s << ' ' << typeIndex << endl; + s << " = " << typeIndex; +} + +static inline void _writeTypeIndexValueLine(QTextStream& s, + const QString& variableName, + int typeIndex) +{ + _writeTypeIndexValue(s, variableName, typeIndex); + s << ",\n"; } -void HeaderGenerator::writeTypeIndexDefineLine(QTextStream& s, const TypeEntry* typeEntry) + +void HeaderGenerator::writeTypeIndexValueLine(QTextStream& s, const TypeEntry* typeEntry) { if (!typeEntry || !typeEntry->generateCode()) return; s.setFieldAlignment(QTextStream::AlignLeft); - int typeIndex = getTypeIndex(typeEntry); - _writeTypeIndexDefineLine(s, getTypeIndexVariableName(typeEntry), typeIndex); + const int typeIndex = typeEntry->sbkIndex(); + _writeTypeIndexValueLine(s, getTypeIndexVariableName(typeEntry), typeIndex); if (typeEntry->isComplex()) { - const ComplexTypeEntry* cType = reinterpret_cast<const ComplexTypeEntry*>(typeEntry); + const ComplexTypeEntry* cType = static_cast<const ComplexTypeEntry*>(typeEntry); if (cType->baseContainerType()) { const AbstractMetaClass *metaClass = AbstractMetaClass::findClass(classes(), cType); if (metaClass->templateBaseClass()) - _writeTypeIndexDefineLine(s, getTypeIndexVariableName(metaClass, true), typeIndex); + _writeTypeIndexValueLine(s, getTypeIndexVariableName(metaClass, true), typeIndex); } } if (typeEntry->isEnum()) { - const EnumTypeEntry* ete = reinterpret_cast<const EnumTypeEntry*>(typeEntry); + const EnumTypeEntry* ete = static_cast<const EnumTypeEntry*>(typeEntry); if (ete->flags()) - writeTypeIndexDefineLine(s, ete->flags()); + writeTypeIndexValueLine(s, ete->flags()); } } -void HeaderGenerator::writeTypeIndexDefine(QTextStream& s, const AbstractMetaClass* metaClass) +void HeaderGenerator::writeTypeIndexValueLines(QTextStream& s, const AbstractMetaClass* metaClass) { if (!metaClass->typeEntry()->generateCode()) return; - writeTypeIndexDefineLine(s, metaClass->typeEntry()); + writeTypeIndexValueLine(s, metaClass->typeEntry()); const AbstractMetaEnumList &enums = metaClass->enums(); for (const AbstractMetaEnum *metaEnum : enums) { if (metaEnum->isPrivate()) continue; - writeTypeIndexDefineLine(s, metaEnum->typeEntry()); + writeTypeIndexValueLine(s, metaEnum->typeEntry()); } } +// Format the typedefs for the typedef entries to be generated +static void formatTypeDefEntries(QTextStream &s) +{ + QVector<const TypedefEntry *> entries; + const auto typeDbEntries = TypeDatabase::instance()->typedefEntries(); + for (auto it = typeDbEntries.cbegin(), end = typeDbEntries.cend(); it != end; ++it) { + if (it.value()->generateCode() != 0) + entries.append(it.value()); + } + if (entries.isEmpty()) + return; + s << "\n// typedef entries\n"; + for (const auto e : entries) { + const QString name = e->qualifiedCppName(); + // Fixme: simplify by using nested namespaces in C++ 17. + const auto components = name.splitRef(QLatin1String("::")); + const int nameSpaceCount = components.size() - 1; + for (int n = 0; n < nameSpaceCount; ++n) + s << "namespace " << components.at(n) << " {\n"; + s << "using " << components.constLast() << " = " << e->sourceType() << ";\n"; + for (int n = 0; n < nameSpaceCount; ++n) + s << "}\n"; + } + s << '\n'; +} + + bool HeaderGenerator::finishGeneration() { // Generate the main header for this module. @@ -342,47 +378,49 @@ bool HeaderGenerator::finishGeneration() Indentation indent(INDENT); - macrosStream << "// Type indices" << endl; + macrosStream << "// Type indices\nenum : int {\n"; AbstractMetaEnumList globalEnums = this->globalEnums(); - const AbstractMetaClassList &classList = classes(); + AbstractMetaClassList classList = classes(); + + std::sort(classList.begin(), classList.end(), [](AbstractMetaClass *a, AbstractMetaClass* b) { + return a->typeEntry()->sbkIndex() < b->typeEntry()->sbkIndex(); + }); + for (const AbstractMetaClass *metaClass : classList) { - writeTypeIndexDefine(macrosStream, metaClass); + writeTypeIndexValueLines(macrosStream, metaClass); lookForEnumsInClassesNotToBeGenerated(globalEnums, metaClass); } for (const AbstractMetaEnum *metaEnum : qAsConst(globalEnums)) - writeTypeIndexDefineLine(macrosStream, metaEnum->typeEntry()); + writeTypeIndexValueLine(macrosStream, metaEnum->typeEntry()); // Write the smart pointer define indexes. int smartPointerCountIndex = getMaxTypeIndex(); int smartPointerCount = 0; const QVector<const AbstractMetaType *> &instantiatedSmartPtrs = instantiatedSmartPointers(); for (const AbstractMetaType *metaType : instantiatedSmartPtrs) { - QString variableName = getTypeIndexVariableName(metaType); - macrosStream << "#define "; - macrosStream.setFieldWidth(60); - macrosStream << variableName; - macrosStream.setFieldWidth(0); - macrosStream << ' ' << smartPointerCountIndex << " // " << metaType->cppSignature() - << endl; + _writeTypeIndexValue(macrosStream, getTypeIndexVariableName(metaType), + smartPointerCountIndex); + macrosStream << ", // " << metaType->cppSignature() << endl; ++smartPointerCountIndex; ++smartPointerCount; } + _writeTypeIndexValue(macrosStream, + QLatin1String("SBK_") + moduleName() + QLatin1String("_IDX_COUNT"), + getMaxTypeIndex() + smartPointerCount); + macrosStream << "\n};\n"; - macrosStream << "#define "; - macrosStream.setFieldWidth(60); - macrosStream << QLatin1String("SBK_") + moduleName() + QLatin1String("_IDX_COUNT"); - macrosStream.setFieldWidth(0); - macrosStream << ' ' << getMaxTypeIndex() + smartPointerCount << endl << endl; macrosStream << "// This variable stores all Python types exported by this module." << endl; macrosStream << "extern PyTypeObject** " << cppApiVariableName() << ';' << endl << endl; + macrosStream << "// This variable stores the Python module object exported by this module." << endl; + macrosStream << "extern PyObject* " << pythonModuleObjectName() << ';' << endl << endl; macrosStream << "// This variable stores all type converters exported by this module." << endl; macrosStream << "extern SbkConverter** " << convertersVariableName() << ';' << endl << endl; // TODO-CONVERTER ------------------------------------------------------------------------------ // Using a counter would not do, a fix must be made to APIExtractor's getTypeIndex(). - macrosStream << "// Converter indices" << endl; + macrosStream << "// Converter indices\nenum : int {\n"; const PrimitiveTypeEntryList &primitives = primitiveTypes(); int pCount = 0; for (const PrimitiveTypeEntry *ptype : primitives) { @@ -393,28 +431,25 @@ bool HeaderGenerator::finishGeneration() if (!ptype->generateCode() || !ptype->customConversion()) continue; - _writeTypeIndexDefineLine(macrosStream, getTypeIndexVariableName(ptype), pCount++); + _writeTypeIndexValueLine(macrosStream, getTypeIndexVariableName(ptype), pCount++); } const QVector<const AbstractMetaType *> &containers = instantiatedContainers(); for (const AbstractMetaType *container : containers) { - //_writeTypeIndexDefineLine(macrosStream, getTypeIndexVariableName(container), pCount); - // DEBUG - QString variableName = getTypeIndexVariableName(container); - macrosStream << "#define "; - macrosStream.setFieldWidth(60); - macrosStream << variableName; - macrosStream.setFieldWidth(0); - macrosStream << ' ' << pCount << " // " << container->cppSignature() << endl; - // DEBUG + _writeTypeIndexValue(macrosStream, getTypeIndexVariableName(container), pCount); + macrosStream << ", // " << container->cppSignature() << endl; pCount++; } // Because on win32 the compiler will not accept a zero length array. if (pCount == 0) pCount++; - _writeTypeIndexDefineLine(macrosStream, QStringLiteral("SBK_%1_CONVERTERS_IDX_COUNT").arg(moduleName()), pCount); - macrosStream << endl; + _writeTypeIndexValue(macrosStream, QStringLiteral("SBK_%1_CONVERTERS_IDX_COUNT") + .arg(moduleName()), pCount); + macrosStream << "\n};\n"; + + formatTypeDefEntries(macrosStream); + // TODO-CONVERTER ------------------------------------------------------------------------------ macrosStream << "// Macros for type check" << endl; @@ -474,12 +509,6 @@ bool HeaderGenerator::finishGeneration() s << "#include <sbkpython.h>" << endl; s << "#include <sbkconverter.h>" << endl; - s << "#include <sbkenum.h>" << endl; - s << "#include <basewrapper.h>" << endl; - s << "#include <bindingmanager.h>" << endl; - s << "#include <memory>" << endl << endl; - if (usePySideExtensions()) - s << "#include <pysidesignal.h>" << endl; QStringList requiredTargetImports = TypeDatabase::instance()->requiredTargetImports(); if (!requiredTargetImports.isEmpty()) { @@ -580,7 +609,7 @@ void HeaderGenerator::writeInheritedOverloads(QTextStream& s) QString argName = arg->name(); const TypeEntry* enumTypeEntry = 0; if (arg->type()->isFlags()) - enumTypeEntry = reinterpret_cast<const FlagsTypeEntry*>(arg->type()->typeEntry())->originator(); + enumTypeEntry = static_cast<const FlagsTypeEntry*>(arg->type()->typeEntry())->originator(); else if (arg->type()->isEnum()) enumTypeEntry = arg->type()->typeEntry(); if (enumTypeEntry) diff --git a/sources/shiboken2/generator/shiboken2/headergenerator.h b/sources/shiboken2/generator/shiboken2/headergenerator.h index acf0448a7..821531aab 100644 --- a/sources/shiboken2/generator/shiboken2/headergenerator.h +++ b/sources/shiboken2/generator/shiboken2/headergenerator.h @@ -55,8 +55,8 @@ private: void writeSbkTypeFunction(QTextStream& s, const AbstractMetaEnum* cppEnum); void writeSbkTypeFunction(QTextStream& s, const AbstractMetaClass* cppClass); void writeSbkTypeFunction(QTextStream &s, const AbstractMetaType *metaType); - void writeTypeIndexDefineLine(QTextStream& s, const TypeEntry* typeEntry); - void writeTypeIndexDefine(QTextStream& s, const AbstractMetaClass* metaClass); + void writeTypeIndexValueLine(QTextStream& s, const TypeEntry* typeEntry); + void writeTypeIndexValueLines(QTextStream& s, const AbstractMetaClass* metaClass); void writeProtectedEnumSurrogate(QTextStream& s, const AbstractMetaEnum* cppEnum); void writeInheritedOverloads(QTextStream& s); diff --git a/sources/shiboken2/generator/shiboken2/overloaddata.cpp b/sources/shiboken2/generator/shiboken2/overloaddata.cpp index 73198ba12..a95603754 100644 --- a/sources/shiboken2/generator/shiboken2/overloaddata.cpp +++ b/sources/shiboken2/generator/shiboken2/overloaddata.cpp @@ -94,8 +94,6 @@ static bool typesAreEqual(const AbstractMetaType* typeA, const AbstractMetaType* */ struct OverloadSortData { - OverloadSortData() : counter(0) {} - /** * Adds a typeName into the type map without associating it with * a OverloadData. This is done to express type dependencies that could @@ -121,7 +119,7 @@ struct OverloadSortData int lastProcessedItemId() { return counter - 1; } - int counter; + int counter = 0; QHash<QString, int> map; // typeName -> id QHash<int, OverloadData*> reverseMap; // id -> OverloadData; }; @@ -149,11 +147,11 @@ static QString getImplicitConversionTypeName(const AbstractMetaType* containerTy for (const AbstractMetaType *otherType : instantiations) types << (otherType == instantiation ? impConv : getTypeName(otherType)); - const ContainerTypeEntry* containerTypeEntry = dynamic_cast<const ContainerTypeEntry*>(containerType->typeEntry()); - return containerTypeEntry->qualifiedCppName() + QLatin1Char('<') + return containerType->typeEntry()->qualifiedCppName() + QLatin1Char('<') + types.join(QLatin1String(", ")) + QLatin1String(" >"); } +// overloaddata.cpp static QString msgCyclicDependency(const QString &funcName, const QString &graphName, const OverloadData::MetaFunctionList &involvedConversions) { @@ -499,7 +497,8 @@ OverloadData::OverloadData(const AbstractMetaFunctionList& overloads, const Shib OverloadData::OverloadData(OverloadData* headOverloadData, const AbstractMetaFunction* func, const AbstractMetaType* argType, int argPos) : m_minArgs(256), m_maxArgs(0), m_argPos(argPos), m_argType(argType), - m_headOverloadData(headOverloadData), m_previousOverloadData(0) + m_headOverloadData(headOverloadData), m_previousOverloadData(nullptr), + m_generator(nullptr) { if (func) this->addOverload(func); @@ -808,8 +807,7 @@ QPair<int, int> OverloadData::getMinMaxArguments(const AbstractMetaFunctionList& { int minArgs = 10000; int maxArgs = 0; - for (int i = 0; i < overloads.size(); i++) { - const AbstractMetaFunction* func = overloads[i]; + for (const AbstractMetaFunction *func : overloads) { int origNumArgs = func->arguments().size(); int removed = numberOfRemovedArguments(func); int numArgs = origNumArgs - removed; @@ -825,7 +823,7 @@ QPair<int, int> OverloadData::getMinMaxArguments(const AbstractMetaFunctionList& minArgs = fixedArgIndex; } } - return QPair<int, int>(minArgs, maxArgs); + return {minArgs, maxArgs}; } bool OverloadData::isSingleArgument(const AbstractMetaFunctionList& overloads) @@ -840,7 +838,7 @@ bool OverloadData::isSingleArgument(const AbstractMetaFunctionList& overloads) return singleArgument; } -void OverloadData::dumpGraph(QString filename) const +void OverloadData::dumpGraph(const QString &filename) const { QFile file(filename); if (file.open(QFile::WriteOnly)) { diff --git a/sources/shiboken2/generator/shiboken2/overloaddata.h b/sources/shiboken2/generator/shiboken2/overloaddata.h index 435c19aa2..4759ca9c3 100644 --- a/sources/shiboken2/generator/shiboken2/overloaddata.h +++ b/sources/shiboken2/generator/shiboken2/overloaddata.h @@ -114,7 +114,7 @@ public: /// Returns true if all overloads have no more than one argument. static bool isSingleArgument(const AbstractMetaFunctionList& overloads); - void dumpGraph(QString filename) const; + void dumpGraph(const QString &filename) const; QString dumpGraph() const; bool hasArgumentTypeReplace() const; diff --git a/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp b/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp index 80096cbf2..b9eea7529 100644 --- a/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp @@ -28,9 +28,11 @@ #include "shibokengenerator.h" #include <abstractmetalang.h> +#include <messages.h> #include "overloaddata.h" #include <reporthandler.h> #include <typedatabase.h> +#include <abstractmetabuilder.h> #include <iostream> #include <QtCore/QDir> @@ -39,13 +41,29 @@ #include <limits> #include <memory> -#define NULL_VALUE "NULL" -#define AVOID_PROTECTED_HACK "avoid-protected-hack" -#define PARENT_CTOR_HEURISTIC "enable-parent-ctor-heuristic" -#define RETURN_VALUE_HEURISTIC "enable-return-value-heuristic" -#define ENABLE_PYSIDE_EXTENSIONS "enable-pyside-extensions" -#define DISABLE_VERBOSE_ERROR_MESSAGES "disable-verbose-error-messages" -#define USE_ISNULL_AS_NB_NONZERO "use-isnull-as-nb_nonzero" +static const char NULL_VALUE[] = "NULL"; +static const char AVOID_PROTECTED_HACK[] = "avoid-protected-hack"; +static const char PARENT_CTOR_HEURISTIC[] = "enable-parent-ctor-heuristic"; +static const char RETURN_VALUE_HEURISTIC[] = "enable-return-value-heuristic"; +static const char ENABLE_PYSIDE_EXTENSIONS[] = "enable-pyside-extensions"; +static const char DISABLE_VERBOSE_ERROR_MESSAGES[] = "disable-verbose-error-messages"; +static const char USE_ISNULL_AS_NB_NONZERO[] = "use-isnull-as-nb_nonzero"; + +const char *CPP_ARG = "cppArg"; +const char *CPP_ARG_REMOVED = "removed_cppArg"; +const char *CPP_RETURN_VAR = "cppResult"; +const char *CPP_SELF_VAR = "cppSelf"; +const char *PYTHON_ARG = "pyArg"; +const char *PYTHON_ARGS = "pyArgs"; +const char *PYTHON_OVERRIDE_VAR = "pyOverride"; +const char *PYTHON_RETURN_VAR = "pyResult"; +const char *PYTHON_TO_CPP_VAR = "pythonToCpp"; +const char *SMART_POINTER_GETTER = "kSmartPointerGetter"; + +const char *CONV_RULE_OUT_VAR_SUFFIX = "_out"; +const char *BEGIN_ALLOW_THREADS = + "PyThreadState* _save = PyEval_SaveThread(); // Py_BEGIN_ALLOW_THREADS"; +const char *END_ALLOW_THREADS = "PyEval_RestoreThread(_save); // Py_END_ALLOW_THREADS"; //static void dumpFunction(AbstractMetaFunctionList lst); @@ -100,7 +118,7 @@ static QString resolveScopePrefix(const AbstractMetaEnum *metaEnum, return resolveScopePrefix(parts, value); } -ShibokenGenerator::ShibokenGenerator() : Generator() +ShibokenGenerator::ShibokenGenerator() { if (m_pythonPrimitiveTypeName.isEmpty()) ShibokenGenerator::initPrimitiveTypesCorrespondences(); @@ -117,17 +135,19 @@ ShibokenGenerator::ShibokenGenerator() : Generator() m_typeSystemConvName[TypeSystemIsConvertibleFunction] = QLatin1String("isConvertible"); m_typeSystemConvName[TypeSystemToCppFunction] = QLatin1String("toCpp"); m_typeSystemConvName[TypeSystemToPythonFunction] = QLatin1String("toPython"); + + const char CHECKTYPE_REGEX[] = R"(%CHECKTYPE\[([^\[]*)\]\()"; + const char ISCONVERTIBLE_REGEX[] = R"(%ISCONVERTIBLE\[([^\[]*)\]\()"; + const char CONVERTTOPYTHON_REGEX[] = R"(%CONVERTTOPYTHON\[([^\[]*)\]\()"; + const char CONVERTTOCPP_REGEX[] = + R"((\*?%?[a-zA-Z_][\w\.]*(?:\[[^\[^<^>]+\])*)(?:\s+)=(?:\s+)%CONVERTTOCPP\[([^\[]*)\]\()"; m_typeSystemConvRegEx[TypeSystemCheckFunction] = QRegularExpression(QLatin1String(CHECKTYPE_REGEX)); m_typeSystemConvRegEx[TypeSystemIsConvertibleFunction] = QRegularExpression(QLatin1String(ISCONVERTIBLE_REGEX)); m_typeSystemConvRegEx[TypeSystemToPythonFunction] = QRegularExpression(QLatin1String(CONVERTTOPYTHON_REGEX)); m_typeSystemConvRegEx[TypeSystemToCppFunction] = QRegularExpression(QLatin1String(CONVERTTOCPP_REGEX)); } -ShibokenGenerator::~ShibokenGenerator() -{ - // TODO-CONVERTER: it must be caching types that were not created here. - //qDeleteAll(m_metaTypeFromStringCache.values()); -} +ShibokenGenerator::~ShibokenGenerator() = default; void ShibokenGenerator::clearTpFuncs() { @@ -277,7 +297,7 @@ bool ShibokenGenerator::shouldGenerateCppWrapper(const AbstractMetaClass* metaCl for (const AbstractMetaFunction *func : funcs) { if (!func->isProtected() || func->isSignal() || func->isModifiedRemoved()) continue; - else if (func->isOperatorOverload()) + if (func->isOperatorOverload()) protectedOperators++; else protectedFunctions++; @@ -332,9 +352,8 @@ QString ShibokenGenerator::wrapperName(const AbstractMetaClass* metaClass) const result += QLatin1String("Wrapper"); return result; - } else { - return metaClass->qualifiedCppName(); } + return metaClass->qualifiedCppName(); } QString ShibokenGenerator::wrapperName(const AbstractMetaType *metaType) const @@ -342,7 +361,19 @@ QString ShibokenGenerator::wrapperName(const AbstractMetaType *metaType) const return metaType->cppSignature(); } -QString ShibokenGenerator::fullPythonFunctionName(const AbstractMetaFunction* func) +QString ShibokenGenerator::fullPythonClassName(const AbstractMetaClass *metaClass) +{ + QString fullClassName = metaClass->name(); + const AbstractMetaClass *enclosing = metaClass->enclosingClass(); + while (enclosing) { + fullClassName.prepend(enclosing->name() + QLatin1Char('.')); + enclosing = enclosing->enclosingClass(); + } + fullClassName.prepend(packageName() + QLatin1Char('.')); + return fullClassName; +} + +QString ShibokenGenerator::fullPythonFunctionName(const AbstractMetaFunction *func) //WS { QString funcName; if (func->isOperatorOverload()) @@ -350,11 +381,11 @@ QString ShibokenGenerator::fullPythonFunctionName(const AbstractMetaFunction* fu else funcName = func->name(); if (func->ownerClass()) { - QString fullName = func->ownerClass()->fullName(); + QString fullClassName = fullPythonClassName(func->ownerClass()); if (func->isConstructor()) - funcName = fullName; + funcName = fullClassName; else - funcName.prepend(fullName + QLatin1Char('.')); + funcName.prepend(fullClassName + QLatin1Char('.')); } else { funcName = packageName() + QLatin1Char('.') + func->name(); @@ -434,7 +465,8 @@ QString ShibokenGenerator::cpythonSetterFunctionName(const AbstractMetaField* me return QStringLiteral("%1_set_%2").arg(cpythonBaseName(metaField->enclosingClass()), metaField->name()); } -static QString cpythonEnumFlagsName(QString moduleName, QString qualifiedCppName) +static QString cpythonEnumFlagsName(const QString &moduleName, + const QString &qualifiedCppName) { QString result = QStringLiteral("Sbk%1_%2").arg(moduleName, qualifiedCppName); result.replace(QLatin1String("::"), QLatin1String("_")); @@ -570,7 +602,7 @@ QString ShibokenGenerator::guessScopeForDefaultValue(const AbstractMetaFunction fieldName.prepend(prefix); prefix.clear(); } else { - fieldName.prepend(QLatin1String(CPP_SELF_VAR "->")); + fieldName.prepend(QLatin1String(CPP_SELF_VAR) + QLatin1String("->")); } value.replace(match.captured(1), fieldName); break; @@ -616,12 +648,14 @@ QString ShibokenGenerator::cpythonSpecialCastFunctionName(const AbstractMetaClas return cpythonBaseName(metaClass->typeEntry()) + QLatin1String("SpecialCastFunction"); } -QString ShibokenGenerator::cpythonWrapperCPtr(const AbstractMetaClass* metaClass, QString argName) +QString ShibokenGenerator::cpythonWrapperCPtr(const AbstractMetaClass* metaClass, + const QString &argName) { return cpythonWrapperCPtr(metaClass->typeEntry(), argName); } -QString ShibokenGenerator::cpythonWrapperCPtr(const AbstractMetaType *metaType, QString argName) +QString ShibokenGenerator::cpythonWrapperCPtr(const AbstractMetaType *metaType, + const QString &argName) { if (!ShibokenGenerator::isWrapperType(metaType->typeEntry())) return QString(); @@ -630,7 +664,8 @@ QString ShibokenGenerator::cpythonWrapperCPtr(const AbstractMetaType *metaType, + QLatin1String(", reinterpret_cast<SbkObject *>(") + argName + QLatin1String(")))"); } -QString ShibokenGenerator::cpythonWrapperCPtr(const TypeEntry* type, QString argName) +QString ShibokenGenerator::cpythonWrapperCPtr(const TypeEntry* type, + const QString &argName) { if (!ShibokenGenerator::isWrapperType(type)) return QString(); @@ -706,7 +741,8 @@ QString ShibokenGenerator::getFormatUnitString(const AbstractMetaFunction* func, || arg->type()->referenceType() == LValueReference) { result += QLatin1Char(objType); } else if (arg->type()->isPrimitive()) { - const PrimitiveTypeEntry* ptype = (const PrimitiveTypeEntry*) arg->type()->typeEntry(); + const PrimitiveTypeEntry *ptype = + static_cast<const PrimitiveTypeEntry *>(arg->type()->typeEntry()); if (ptype->basicReferencedTypeEntry()) ptype = ptype->basicReferencedTypeEntry(); if (m_formatUnits.contains(ptype->name())) @@ -745,7 +781,7 @@ QString ShibokenGenerator::cpythonBaseName(const TypeEntry* type) if (ShibokenGenerator::isWrapperType(type) || type->isNamespace()) { // && type->referenceType() == NoReference) { baseName = QLatin1String("Sbk_") + type->name(); } else if (type->isPrimitive()) { - const PrimitiveTypeEntry* ptype = (const PrimitiveTypeEntry*) type; + const PrimitiveTypeEntry *ptype = static_cast<const PrimitiveTypeEntry *>(type); while (ptype->basicReferencedTypeEntry()) ptype = ptype->basicReferencedTypeEntry(); if (ptype->targetLangApiName() == ptype->name()) @@ -753,11 +789,11 @@ QString ShibokenGenerator::cpythonBaseName(const TypeEntry* type) else baseName = ptype->targetLangApiName(); } else if (type->isEnum()) { - baseName = cpythonEnumName((const EnumTypeEntry*) type); + baseName = cpythonEnumName(static_cast<const EnumTypeEntry *>(type)); } else if (type->isFlags()) { - baseName = cpythonFlagsName((const FlagsTypeEntry*) type); + baseName = cpythonFlagsName(static_cast<const FlagsTypeEntry *>(type)); } else if (type->isContainer()) { - const ContainerTypeEntry* ctype = (const ContainerTypeEntry*) type; + const ContainerTypeEntry *ctype = static_cast<const ContainerTypeEntry *>(type); switch (ctype->type()) { case ContainerTypeEntry::ListContainer: case ContainerTypeEntry::StringListContainer: @@ -858,14 +894,6 @@ QString ShibokenGenerator::cpythonTypeNameExt(const AbstractMetaType* type) + getTypeIndexVariableName(type) + QLatin1Char(']'); } -static QString msgUnknownOperator(const AbstractMetaFunction* func) -{ - QString result = QLatin1String("Unknown operator: \"") + func->originalName() + QLatin1Char('"'); - if (const AbstractMetaClass *c = func->implementingClass()) - result += QLatin1String(" in class: ") + c->name(); - return result; -} - static inline QString unknownOperator() { return QStringLiteral("__UNKNOWN_OPERATOR__"); } QString ShibokenGenerator::fixedCppTypeName(const CustomConversion::TargetToNativeConversion* toNative) @@ -925,7 +953,7 @@ QString ShibokenGenerator::pythonPrimitiveTypeName(const PrimitiveTypeEntry* typ return pythonPrimitiveTypeName(type->name()); } -QString ShibokenGenerator::pythonOperatorFunctionName(QString cppOpFuncName) +QString ShibokenGenerator::pythonOperatorFunctionName(const QString &cppOpFuncName) { QString value = m_pythonOperators.value(cppOpFuncName); if (value.isEmpty()) @@ -953,7 +981,7 @@ QString ShibokenGenerator::pythonOperatorFunctionName(const AbstractMetaFunction return op; } -QString ShibokenGenerator::pythonRichCompareOperatorId(QString cppOpFuncName) +QString ShibokenGenerator::pythonRichCompareOperatorId(const QString &cppOpFuncName) { return QLatin1String("Py_") + m_pythonOperators.value(cppOpFuncName).toUpper(); } @@ -963,7 +991,7 @@ QString ShibokenGenerator::pythonRichCompareOperatorId(const AbstractMetaFunctio return pythonRichCompareOperatorId(func->originalName()); } -bool ShibokenGenerator::isNumber(QString cpythonApiName) +bool ShibokenGenerator::isNumber(const QString &cpythonApiName) { return cpythonApiName == QLatin1String("PyInt") || cpythonApiName == QLatin1String("PyFloat") @@ -975,7 +1003,7 @@ bool ShibokenGenerator::isNumber(const TypeEntry* type) { if (!type->isPrimitive()) return false; - return isNumber(pythonPrimitiveTypeName((const PrimitiveTypeEntry*) type)); + return isNumber(pythonPrimitiveTypeName(static_cast<const PrimitiveTypeEntry *>(type))); } bool ShibokenGenerator::isNumber(const AbstractMetaType* type) @@ -987,7 +1015,8 @@ bool ShibokenGenerator::isPyInt(const TypeEntry* type) { if (!type->isPrimitive()) return false; - return pythonPrimitiveTypeName((const PrimitiveTypeEntry*) type) == QLatin1String("PyInt"); + return pythonPrimitiveTypeName(static_cast<const PrimitiveTypeEntry *>(type)) + == QLatin1String("PyInt"); } bool ShibokenGenerator::isPyInt(const AbstractMetaType* type) @@ -998,7 +1027,7 @@ bool ShibokenGenerator::isPyInt(const AbstractMetaType* type) bool ShibokenGenerator::isWrapperType(const TypeEntry* type) { if (type->isComplex()) - return ShibokenGenerator::isWrapperType((const ComplexTypeEntry*)type); + return ShibokenGenerator::isWrapperType(static_cast<const ComplexTypeEntry *>(type)); return type->isObject() || type->isValue() || type->isSmartPointer(); } bool ShibokenGenerator::isWrapperType(const ComplexTypeEntry* type) @@ -1052,7 +1081,7 @@ bool ShibokenGenerator::isUserPrimitive(const TypeEntry* type) { if (!type->isPrimitive()) return false; - const PrimitiveTypeEntry* trueType = (const PrimitiveTypeEntry*) type; + const PrimitiveTypeEntry *trueType = static_cast<const PrimitiveTypeEntry *>(type); if (trueType->basicReferencedTypeEntry()) trueType = trueType->basicReferencedTypeEntry(); return trueType->isPrimitive() && !trueType->isCppPrimitive() @@ -1072,7 +1101,7 @@ bool ShibokenGenerator::isCppPrimitive(const TypeEntry* type) return true; if (!type->isPrimitive()) return false; - const PrimitiveTypeEntry* trueType = (const PrimitiveTypeEntry*) type; + const PrimitiveTypeEntry *trueType = static_cast<const PrimitiveTypeEntry *>(type); if (trueType->basicReferencedTypeEntry()) trueType = trueType->basicReferencedTypeEntry(); return trueType->qualifiedCppName() == QLatin1String("std::string"); @@ -1125,9 +1154,11 @@ QString ShibokenGenerator::cpythonCheckFunction(const AbstractMetaType* metaType if (isVoidPointer(metaType)) return QLatin1String("PyObject_Check"); return cpythonCheckFunction(metaType->typeEntry(), genericNumberType); - } else if (metaType->typeEntry()->isContainer()) { + } + if (metaType->typeEntry()->isContainer()) { QString typeCheck = QLatin1String("Shiboken::Conversions::"); - ContainerTypeEntry::Type type = ((const ContainerTypeEntry*)metaType->typeEntry())->type(); + ContainerTypeEntry::Type type = + static_cast<const ContainerTypeEntry *>(metaType->typeEntry())->type(); if (type == ContainerTypeEntry::ListContainer || type == ContainerTypeEntry::StringListContainer || type == ContainerTypeEntry::LinkedListContainer @@ -1154,8 +1185,8 @@ QString ShibokenGenerator::cpythonCheckFunction(const AbstractMetaType* metaType const AbstractMetaType* firstType = metaType->instantiations().constFirst(); const AbstractMetaType* secondType = metaType->instantiations().constLast(); if (isPointerToWrapperType(firstType) && isPointerToWrapperType(secondType)) { - typeCheck += QString::fromLatin1("check%1Types(%2, %3, ").arg(pyType) - .arg(cpythonTypeNameExt(firstType), cpythonTypeNameExt(secondType)); + typeCheck += QString::fromLatin1("check%1Types(%2, %3, ") + .arg(pyType, cpythonTypeNameExt(firstType), cpythonTypeNameExt(secondType)); } else { typeCheck += QString::fromLatin1("convertible%1Types(%2, %3, %4, %5, ") .arg(pyType, converterObject(firstType), @@ -1182,8 +1213,10 @@ QString ShibokenGenerator::cpythonCheckFunction(const TypeEntry* type, bool gene if (type->isEnum() || type->isFlags() || isWrapperType(type)) return QString::fromLatin1("SbkObject_TypeCheck(%1, ").arg(cpythonTypeNameExt(type)); - else if (isCppPrimitive(type)) - return pythonPrimitiveTypeName((const PrimitiveTypeEntry*)type) + QLatin1String("_Check"); + if (isCppPrimitive(type)) { + return pythonPrimitiveTypeName(static_cast<const PrimitiveTypeEntry *>(type)) + + QLatin1String("_Check"); + } QString typeCheck; if (type->targetLangApiName() == type->name()) typeCheck = cpythonIsConvertibleFunction(type); @@ -1392,7 +1425,7 @@ void ShibokenGenerator::writeFunctionArguments(QTextStream &s, if (options & Generator::WriteSelf) { s << func->implementingClass()->name() << '&'; if (!(options & SkipName)) - s << " " PYTHON_SELF_VAR; + s << " self"; } int argUsed = 0; @@ -1412,13 +1445,12 @@ QString ShibokenGenerator::functionReturnType(const AbstractMetaFunction* func, QString modifiedReturnType = QString(func->typeReplaced(0)); if (!modifiedReturnType.isNull() && !(options & OriginalTypeDescription)) return modifiedReturnType; - else - return translateType(func->type(), func->implementingClass(), options); + return translateType(func->type(), func->implementingClass(), options); } QString ShibokenGenerator::functionSignature(const AbstractMetaFunction *func, - QString prepend, - QString append, + const QString &prepend, + const QString &append, Options options, int /* argCount */) const { @@ -1619,8 +1651,9 @@ ShibokenGenerator::ArgumentVarReplacementList ShibokenGenerator::getArgumentRepl QString argValue; if (language == TypeSystem::TargetLangCode) { bool hasConversionRule = !func->conversionRule(convLang, i+1).isEmpty(); - bool argRemoved = func->argumentRemoved(i+1); - removed = removed + (int) argRemoved; + const bool argRemoved = func->argumentRemoved(i+1); + if (argRemoved) + ++removed; if (argRemoved && hasConversionRule) argValue = arg->name() + QLatin1String(CONV_RULE_OUT_VAR_SUFFIX); else if (argRemoved || (lastArg && arg->argumentIndex() > lastArg->argumentIndex())) @@ -1636,8 +1669,7 @@ ShibokenGenerator::ArgumentVarReplacementList ShibokenGenerator::getArgumentRepl } if (type->typeEntry()->isCustom()) { argValue = usePyArgs - ? QString::fromLatin1(PYTHON_ARGS "[%1]").arg(argPos) - : QLatin1String(PYTHON_ARG); + ? pythonArgsAt(argPos) : QLatin1String(PYTHON_ARG); } else { argValue = hasConversionRule ? arg->name() + QLatin1String(CONV_RULE_OUT_VAR_SUFFIX) @@ -1673,17 +1705,6 @@ void ShibokenGenerator::writeCodeSnips(QTextStream& s, s << INDENT << "// End of code injection" << endl; } -static QString msgWrongIndex(const char *varName, const QString &capture, const AbstractMetaFunction *func) -{ - QString result; - QTextStream str(&result); - str << "Wrong index for " << varName << " variable (" << capture << ") on "; - if (const AbstractMetaClass *c = func->implementingClass()) - str << c->name() << "::"; - str << func->signature(); - return result; -} - void ShibokenGenerator::writeCodeSnips(QTextStream& s, const CodeSnipList& codeSnips, TypeSystem::CodeSnipPosition position, @@ -1712,7 +1733,7 @@ void ShibokenGenerator::writeCodeSnips(QTextStream& s, Q_ASSERT(pyArgsRegex.isValid()); if (language == TypeSystem::TargetLangCode) { if (usePyArgs) { - code.replace(pyArgsRegex, QLatin1String(PYTHON_ARGS"[\\1-1]")); + code.replace(pyArgsRegex, QLatin1String(PYTHON_ARGS) + QLatin1String("[\\1-1]")); } else { static const QRegularExpression pyArgsRegexCheck(QStringLiteral("%PYARG_([2-9]+)")); Q_ASSERT(pyArgsRegexCheck.isValid()); @@ -1729,8 +1750,10 @@ void ShibokenGenerator::writeCodeSnips(QTextStream& s, // Python argument on the binding virtual method. static const QRegularExpression pyArgsAttributionRegex(QStringLiteral("%PYARG_(\\d+)\\s*=[^=]\\s*([^;]+)")); Q_ASSERT(pyArgsAttributionRegex.isValid()); - code.replace(pyArgsAttributionRegex, QLatin1String("PyTuple_SET_ITEM(" PYTHON_ARGS ", \\1-1, \\2)")); - code.replace(pyArgsRegex, QLatin1String("PyTuple_GET_ITEM(" PYTHON_ARGS ", \\1-1)")); + code.replace(pyArgsAttributionRegex, QLatin1String("PyTuple_SET_ITEM(") + + QLatin1String(PYTHON_ARGS) + QLatin1String(", \\1-1, \\2)")); + code.replace(pyArgsRegex, QLatin1String("PyTuple_GET_ITEM(") + + QLatin1String(PYTHON_ARGS) + QLatin1String(", \\1-1)")); } // Replace %ARG#_TYPE variables. @@ -1763,7 +1786,8 @@ void ShibokenGenerator::writeCodeSnips(QTextStream& s, } // Replace template variable for self Python object. - QString pySelf = (language == TypeSystem::NativeCode) ? QLatin1String("pySelf") : QLatin1String(PYTHON_SELF_VAR); + QString pySelf = language == TypeSystem::NativeCode + ? QLatin1String("pySelf") : QLatin1String("self"); code.replace(QLatin1String("%PYSELF"), pySelf); // Replace template variable for a pointer to C++ of this object. @@ -1960,16 +1984,6 @@ static QString getConverterTypeSystemVariableArgument(const QString& code, int p } typedef QPair<QString, QString> StringPair; -static QString msgCannotFindType(const QString &type, const QString &variable, - const QString &why) -{ - QString result; - QTextStream(&result) << "Could not find type '" - << type << "' for use in '" << variable << "' conversion: " << why - << "\nMake sure to use the full C++ name, e.g. 'Namespace::Class'."; - return result; -} - void ShibokenGenerator::replaceConverterTypeSystemVariable(TypeSystemConverterVariable converterVariable, QString& code) { QVector<StringPair> replacements; @@ -1978,7 +1992,7 @@ void ShibokenGenerator::replaceConverterTypeSystemVariable(TypeSystemConverterVa const QRegularExpressionMatch match = rit.next(); const QStringList list = match.capturedTexts(); QString conversionString = list.constFirst(); - QString conversionTypeName = list.constLast(); + const QString &conversionTypeName = list.constLast(); QString message; const AbstractMetaType *conversionType = buildAbstractMetaTypeFromString(conversionTypeName, &message); if (!conversionType) { @@ -2002,8 +2016,8 @@ void ShibokenGenerator::replaceConverterTypeSystemVariable(TypeSystemConverterVa QString varName = list.at(1).trimmed(); if (!varType.isEmpty()) { if (varType != conversionType->cppSignature()) { - qFatal(qPrintable(QString::fromLatin1("Types of receiver variable ('%1') and %CONVERTTOCPP type system variable ('%2') differ.") - .arg(varType, conversionType->cppSignature())), NULL); + qFatal("Types of receiver variable ('%s') and %%CONVERTTOCPP type system variable ('%s') differ.", + qPrintable(varType), qPrintable(conversionType->cppSignature())); } c << getFullTypeName(conversionType) << ' ' << varName; writeMinimalConstructorExpression(c, conversionType); @@ -2032,18 +2046,21 @@ void ShibokenGenerator::replaceConverterTypeSystemVariable(TypeSystemConverterVa c << '('; break; } + Q_FALLTHROUGH(); case TypeSystemIsConvertibleFunction: if (conversion.isEmpty()) conversion = cpythonIsConvertibleFunction(conversionType); + Q_FALLTHROUGH(); case TypeSystemToPythonFunction: if (conversion.isEmpty()) conversion = cpythonToPythonConversionFunction(conversionType); + Q_FALLTHROUGH(); default: { QString arg = getConverterTypeSystemVariableArgument(code, match.capturedEnd()); conversionString += arg; if (converterVariable == TypeSystemToPythonFunction && !isVariable(arg)) { - qFatal(qPrintable(QString::fromLatin1("Only variables are acceptable as argument to %%CONVERTTOPYTHON type system variable on code snippet: '%1'") - .arg(code)), NULL); + qFatal("Only variables are acceptable as argument to %%CONVERTTOPYTHON type system variable on code snippet: '%s'", + qPrintable(code)); } if (conversion.contains(QLatin1String("%in"))) { conversion.prepend(QLatin1Char('(')); @@ -2172,9 +2189,7 @@ bool ShibokenGenerator::classNeedsSetattroFunction(const AbstractMetaClass *meta { if (!metaClass) return false; - if (metaClass->typeEntry()->isSmartPointer()) - return true; - return false; + return metaClass->typeEntry()->isSmartPointer(); } AbstractMetaFunctionList ShibokenGenerator::getMethodsWithBothStaticAndNonStaticMethods(const AbstractMetaClass* metaClass) @@ -2244,9 +2259,7 @@ AbstractMetaClassList ShibokenGenerator::getAllAncestors(const AbstractMetaClass QString ShibokenGenerator::getModuleHeaderFileName(const QString& moduleName) const { - QString result = moduleName.isEmpty() ? packageName() : moduleName; - result.replace(QLatin1Char('.'), QLatin1Char('_')); - return result.toLower() + QLatin1String("_python.h"); + return moduleCppPrefix(moduleName).toLower() + QLatin1String("_python.h"); } bool ShibokenGenerator::isCopyable(const AbstractMetaClass *metaClass) @@ -2254,12 +2267,10 @@ bool ShibokenGenerator::isCopyable(const AbstractMetaClass *metaClass) { if (metaClass->isNamespace() || isObjectType(metaClass)) return false; - else if (metaClass->typeEntry()->copyable() == ComplexTypeEntry::Unknown) + if (metaClass->typeEntry()->copyable() == ComplexTypeEntry::Unknown) return metaClass->hasCloneOperator(); - else - return (metaClass->typeEntry()->copyable() == ComplexTypeEntry::CopyableSet); - return false; + return metaClass->typeEntry()->copyable() == ComplexTypeEntry::CopyableSet; } AbstractMetaType *ShibokenGenerator::buildAbstractMetaTypeFromString(QString typeSignature, @@ -2269,110 +2280,18 @@ AbstractMetaType *ShibokenGenerator::buildAbstractMetaTypeFromString(QString typ if (typeSignature.startsWith(QLatin1String("::"))) typeSignature.remove(0, 2); - if (m_metaTypeFromStringCache.contains(typeSignature)) - return m_metaTypeFromStringCache.value(typeSignature); - - QString typeString = typeSignature; - bool isConst = typeString.startsWith(QLatin1String("const ")); - if (isConst) - typeString.remove(0, sizeof("const ") / sizeof(char) - 1); - - ReferenceType refType = NoReference; - if (typeString.endsWith(QLatin1String("&&"))) { - refType = RValueReference; - typeString.chop(2); - typeString = typeString.trimmed(); - } else if (typeString.endsWith(QLatin1Char('&'))) { - refType = LValueReference; - typeString.chop(1); - typeString = typeString.trimmed(); - } - - int indirections = 0; - while (typeString.endsWith(QLatin1Char('*'))) { - ++indirections; - typeString.chop(1); - typeString = typeString.trimmed(); - } - - if (typeString.startsWith(QLatin1String("::"))) - typeString.remove(0, 2); - - QString adjustedTypeName = typeString; - AbstractMetaTypeList instantiations; - int lpos = typeString.indexOf(QLatin1Char('<')); - if (lpos > -1) { - QStringList instantiatedTypes; - int rpos = typeString.lastIndexOf(QLatin1Char('>')); - if ((lpos != -1) && (rpos != -1)) { - QString type = typeString.mid(lpos + 1, rpos - lpos - 1); - int depth = 0; - int start = 0; - for (int i = 0; i < type.count(); ++i) { - if (type.at(i) == QLatin1Char('<')) { - ++depth; - } else if (type.at(i) == QLatin1Char('>')) { - --depth; - } else if (type.at(i) == QLatin1Char(',') && depth == 0) { - instantiatedTypes << type.mid(start, i - start).trimmed(); - start = i + 1; - } - } - instantiatedTypes << type.mid(start).trimmed(); - adjustedTypeName.truncate(lpos); - } - for (const QString &instantiatedType : qAsConst(instantiatedTypes)) { - AbstractMetaType *tmplArgType = buildAbstractMetaTypeFromString(instantiatedType); - if (!tmplArgType) { - if (errorMessage) { - QTextStream(errorMessage) << "Cannot find template type \"" - << instantiatedType << "\" for \"" << typeSignature << "\"."; - } - return nullptr; - } - instantiations.append(tmplArgType); - } - } - - TypeEntry *typeEntry = nullptr; - AbstractMetaType::TypeUsagePattern pattern = AbstractMetaType::InvalidPattern; - - if (instantiations.size() == 1 - && instantiations.at(0)->typeUsagePattern() == AbstractMetaType::EnumPattern - && adjustedTypeName == QLatin1String("QFlags")) { - pattern = AbstractMetaType::FlagsPattern; - typeEntry = TypeDatabase::instance()->findType(typeSignature); - } else { - typeEntry = TypeDatabase::instance()->findType(adjustedTypeName); - } - - if (!typeEntry) { - if (errorMessage) { - QTextStream(errorMessage) << "Cannot find type \"" << adjustedTypeName - << "\" for \"" << typeSignature << "\"."; + auto it = m_metaTypeFromStringCache.find(typeSignature); + if (it == m_metaTypeFromStringCache.end()) { + AbstractMetaType *metaType = + AbstractMetaBuilder::translateType(typeSignature, nullptr, true, errorMessage); + if (Q_UNLIKELY(!metaType)) { + if (errorMessage) + errorMessage->prepend(msgCannotBuildMetaType(typeSignature)); + return nullptr; } - return nullptr; + it = m_metaTypeFromStringCache.insert(typeSignature, metaType); } - - AbstractMetaType *metaType = new AbstractMetaType(); - metaType->setTypeEntry(typeEntry); - metaType->setIndirections(indirections); - metaType->setReferenceType(refType); - metaType->setConstant(isConst); - metaType->setTypeUsagePattern(AbstractMetaType::ContainerPattern); - switch (pattern) { - case AbstractMetaType::FlagsPattern: - metaType->setTypeUsagePattern(pattern); - break; - default: - metaType->setInstantiations(instantiations); - metaType->setTypeUsagePattern(AbstractMetaType::ContainerPattern); - metaType->decideUsagePattern(); - break; - } - - m_metaTypeFromStringCache.insert(typeSignature, metaType); - return metaType; + return it.value(); } AbstractMetaType* ShibokenGenerator::buildAbstractMetaTypeFromTypeEntry(const TypeEntry* typeEntry) @@ -2384,7 +2303,7 @@ AbstractMetaType* ShibokenGenerator::buildAbstractMetaTypeFromTypeEntry(const Ty return m_metaTypeFromStringCache.value(typeName); AbstractMetaType* metaType = new AbstractMetaType; metaType->setTypeEntry(typeEntry); - metaType->setIndirections(0); + metaType->clearIndirections(); metaType->setReferenceType(NoReference); metaType->setConstant(false); metaType->decideUsagePattern(); @@ -2449,7 +2368,7 @@ AbstractMetaFunctionList ShibokenGenerator::getInheritedOverloads(const Abstract { AbstractMetaFunctionList results; AbstractMetaClass* basis; - if (func->ownerClass() && (basis = func->ownerClass()->baseClass(), basis)) { + if (func->ownerClass() && (basis = func->ownerClass()->baseClass())) { for (; basis; basis = basis->baseClass()) { const AbstractMetaFunction* inFunc = basis->findFunction(func->name()); if (inFunc && !seen->contains(inFunc->minimalSignature())) { @@ -2509,6 +2428,23 @@ Generator::OptionDescriptions ShibokenGenerator::options() const "the value of boolean casts")); } +bool ShibokenGenerator::handleOption(const QString &key, const QString & /* value */) +{ + if (key == QLatin1String(PARENT_CTOR_HEURISTIC)) + return (m_useCtorHeuristic = true); + if (key == QLatin1String(ENABLE_PYSIDE_EXTENSIONS)) + return (m_usePySideExtensions = true); + if (key == QLatin1String(RETURN_VALUE_HEURISTIC)) + return (m_userReturnValueHeuristic = true); + if (key == QLatin1String(DISABLE_VERBOSE_ERROR_MESSAGES)) + return (m_verboseErrorMessagesDisabled = true); + if (key == QLatin1String(USE_ISNULL_AS_NB_NONZERO)) + return (m_useIsNullAsNbNonZero = true); + if (key == QLatin1String(AVOID_PROTECTED_HACK)) + return (m_avoidProtectedHack = true); + return false; +} + static void getCode(QStringList& code, const CodeSnipList& codeSnips) { for (const CodeSnip &snip : qAsConst(codeSnips)) @@ -2534,15 +2470,8 @@ static void getCode(QStringList& code, const TypeEntry* type) code.append(toNative->conversion()); } -bool ShibokenGenerator::doSetup(const QMap<QString, QString>& args) +bool ShibokenGenerator::doSetup() { - m_useCtorHeuristic = args.contains(QLatin1String(PARENT_CTOR_HEURISTIC)); - m_usePySideExtensions = args.contains(QLatin1String(ENABLE_PYSIDE_EXTENSIONS)); - m_userReturnValueHeuristic = args.contains(QLatin1String(RETURN_VALUE_HEURISTIC)); - m_verboseErrorMessagesDisabled = args.contains(QLatin1String(DISABLE_VERBOSE_ERROR_MESSAGES)); - m_useIsNullAsNbNonZero = args.contains(QLatin1String(USE_ISNULL_AS_NB_NONZERO)); - m_avoidProtectedHack = args.contains(QLatin1String(AVOID_PROTECTED_HACK)); - TypeDatabase* td = TypeDatabase::instance(); QStringList snips; const PrimitiveTypeEntryList &primitiveTypeList = primitiveTypes(); @@ -2554,7 +2483,11 @@ bool ShibokenGenerator::doSetup(const QMap<QString, QString>& args) const AbstractMetaClassList &classList = classes(); for (const AbstractMetaClass *metaClass : classList) getCode(snips, metaClass->typeEntry()); - getCode(snips, td->findType(packageName())); + + const TypeSystemTypeEntry *moduleEntry = td->findTypeSystemType(packageName()); + Q_ASSERT(moduleEntry); + getCode(snips, moduleEntry); + const FunctionGroupMap &functionGroups = getFunctionGroups(); for (FunctionGroupMapIt it = functionGroups.cbegin(), end = functionGroups.cend(); it != end; ++it) { for (AbstractMetaFunction *func : it.value()) @@ -2611,15 +2544,25 @@ bool ShibokenGenerator::avoidProtectedHack() const return m_avoidProtectedHack; } -QString ShibokenGenerator::cppApiVariableName(const QString& moduleName) const -{ - QString result = moduleName.isEmpty() ? ShibokenGenerator::packageName() : moduleName; +QString ShibokenGenerator::moduleCppPrefix(const QString& moduleName) const + { + QString result = moduleName.isEmpty() ? packageName() : moduleName; result.replace(QLatin1Char('.'), QLatin1Char('_')); - result.prepend(QLatin1String("Sbk")); - result.append(QLatin1String("Types")); return result; } +QString ShibokenGenerator::cppApiVariableName(const QString& moduleName) const +{ + return QLatin1String("Sbk") + moduleCppPrefix(moduleName) + + QLatin1String("Types"); +} + +QString ShibokenGenerator::pythonModuleObjectName(const QString& moduleName) const +{ + return QLatin1String("Sbk") + moduleCppPrefix(moduleName) + + QLatin1String("ModuleObject"); +} + QString ShibokenGenerator::convertersVariableName(const QString& moduleName) const { QString result = cppApiVariableName(moduleName); @@ -2639,35 +2582,50 @@ static QString processInstantiationsVariableName(const AbstractMetaType* type) } return res; } + +static void appendIndexSuffix(QString *s) +{ + if (!s->endsWith(QLatin1Char('_'))) + s->append(QLatin1Char('_')); + s->append(QStringLiteral("IDX")); +} + QString ShibokenGenerator::getTypeIndexVariableName(const AbstractMetaClass* metaClass, bool alternativeTemplateName) { if (alternativeTemplateName) { const AbstractMetaClass* templateBaseClass = metaClass->templateBaseClass(); if (!templateBaseClass) return QString(); - QString base = _fixedCppTypeName(templateBaseClass->typeEntry()->qualifiedCppName()).toUpper(); - QString instantiations; + QString result = QLatin1String("SBK_") + + _fixedCppTypeName(templateBaseClass->typeEntry()->qualifiedCppName()).toUpper(); const AbstractMetaTypeList &templateBaseClassInstantiations = metaClass->templateBaseClassInstantiations(); for (const AbstractMetaType *instantiation : templateBaseClassInstantiations) - instantiations += processInstantiationsVariableName(instantiation); - return QString::fromLatin1("SBK_%1%2_IDX").arg(base, instantiations); + result += processInstantiationsVariableName(instantiation); + appendIndexSuffix(&result); + return result; } return getTypeIndexVariableName(metaClass->typeEntry()); } QString ShibokenGenerator::getTypeIndexVariableName(const TypeEntry* type) { if (type->isCppPrimitive()) { - const PrimitiveTypeEntry* trueType = (const PrimitiveTypeEntry*) type; + const PrimitiveTypeEntry *trueType = static_cast<const PrimitiveTypeEntry*>(type); if (trueType->basicReferencedTypeEntry()) type = trueType->basicReferencedTypeEntry(); } - return QString::fromLatin1("SBK_%1_IDX").arg(_fixedCppTypeName(type->qualifiedCppName()).toUpper()); + QString result = QLatin1String("SBK_") + + _fixedCppTypeName(type->qualifiedCppName()).toUpper(); + appendIndexSuffix(&result); + return result; } QString ShibokenGenerator::getTypeIndexVariableName(const AbstractMetaType* type) { - return QString::fromLatin1("SBK%1%2_IDX") - .arg(type->typeEntry()->isContainer() ? QLatin1Char('_') + moduleName().toUpper() : QString(), - processInstantiationsVariableName(type)); + QString result = QLatin1String("SBK"); + if (type->typeEntry()->isContainer()) + result += QLatin1Char('_') + moduleName().toUpper(); + result += processInstantiationsVariableName(type); + appendIndexSuffix(&result); + return result; } bool ShibokenGenerator::verboseErrorMessagesDisabled() const @@ -2707,30 +2665,37 @@ QString ShibokenGenerator::getDefaultValue(const AbstractMetaFunction* func, co void ShibokenGenerator::writeMinimalConstructorExpression(QTextStream& s, const AbstractMetaType* type, const QString& defaultCtor) { - if (defaultCtor.isEmpty() && isCppPrimitive(type)) + if (!defaultCtor.isEmpty()) { + s << " = " << defaultCtor; + return; + } + if (isCppPrimitive(type)) return; - QString ctor = defaultCtor.isEmpty() ? minimalConstructor(type) : defaultCtor; - if (ctor.isEmpty()) { + const auto ctor = minimalConstructor(type); + if (ctor.isValid()) { + s << ctor.initialization(); + } else { const QString message = msgCouldNotFindMinimalConstructor(QLatin1String(__FUNCTION__), type->cppSignature()); qCWarning(lcShiboken()).noquote() << message; s << ";\n#error " << message << '\n'; - } else { - s << " = " << ctor; } } void ShibokenGenerator::writeMinimalConstructorExpression(QTextStream& s, const TypeEntry* type, const QString& defaultCtor) { - if (defaultCtor.isEmpty() && isCppPrimitive(type)) + if (!defaultCtor.isEmpty()) { + s << " = " << defaultCtor; + return; + } + if (isCppPrimitive(type)) return; - QString ctor = defaultCtor.isEmpty() ? minimalConstructor(type) : defaultCtor; - - if (ctor.isEmpty()) { + const auto ctor = minimalConstructor(type); + if (ctor.isValid()) { + s << ctor.initialization(); + } else { const QString message = msgCouldNotFindMinimalConstructor(QLatin1String(__FUNCTION__), type->qualifiedCppName()); qCWarning(lcShiboken()).noquote() << message; s << ";\n#error " << message << endl; - } else { - s << " = " << ctor; } } @@ -2738,7 +2703,7 @@ bool ShibokenGenerator::isCppIntegralPrimitive(const TypeEntry* type) { if (!type->isCppPrimitive()) return false; - const PrimitiveTypeEntry* trueType = (const PrimitiveTypeEntry*) type; + const PrimitiveTypeEntry *trueType = static_cast<const PrimitiveTypeEntry *>(type); if (trueType->basicReferencedTypeEntry()) trueType = trueType->basicReferencedTypeEntry(); QString typeName = trueType->qualifiedCppName(); @@ -2751,8 +2716,8 @@ bool ShibokenGenerator::isCppIntegralPrimitive(const AbstractMetaType* type) return isCppIntegralPrimitive(type->typeEntry()); } -QString ShibokenGenerator::msgCouldNotFindMinimalConstructor(const QString &where, const QString &type) +QString ShibokenGenerator::pythonArgsAt(int i) { - return where + QLatin1String(": Could not find a minimal constructor for type '") + type - + QLatin1String("'. This will result in a compilation error."); + return QLatin1String(PYTHON_ARGS) + QLatin1Char('[') + + QString::number(i) + QLatin1Char(']'); } diff --git a/sources/shiboken2/generator/shiboken2/shibokengenerator.h b/sources/shiboken2/generator/shiboken2/shibokengenerator.h index cb1bdd11f..60e31a99b 100644 --- a/sources/shiboken2/generator/shiboken2/shibokengenerator.h +++ b/sources/shiboken2/generator/shiboken2/shibokengenerator.h @@ -29,28 +29,20 @@ #ifndef SHIBOKENGENERATOR_H #define SHIBOKENGENERATOR_H -#define CONV_RULE_OUT_VAR_SUFFIX "_out" -#define CPP_ARG "cppArg" -#define CPP_ARG0 CPP_ARG"0" -#define CPP_ARG_REMOVED "removed_" CPP_ARG -#define CPP_RETURN_VAR "cppResult" -#define CPP_SELF_VAR "cppSelf" -#define PYTHON_ARG "pyArg" -#define PYTHON_ARGS PYTHON_ARG "s" -#define PYTHON_OVERRIDE_VAR "pyOverride" -#define PYTHON_RETURN_VAR "pyResult" -#define PYTHON_SELF_VAR "self" -#define THREAD_STATE_SAVER_VAR "threadStateSaver" -#define BEGIN_ALLOW_THREADS "PyThreadState* _save = PyEval_SaveThread(); // Py_BEGIN_ALLOW_THREADS" -#define END_ALLOW_THREADS "PyEval_RestoreThread(_save); // Py_END_ALLOW_THREADS" -#define PYTHON_TO_CPP_VAR "pythonToCpp" -#define SMART_POINTER_GETTER "kSmartPointerGetter" - -#define CHECKTYPE_REGEX "%CHECKTYPE\\[([^\\[]*)\\]\\(" -#define ISCONVERTIBLE_REGEX "%ISCONVERTIBLE\\[([^\\[]*)\\]\\(" -#define CONVERTTOPYTHON_REGEX "%CONVERTTOPYTHON\\[([^\\[]*)\\]\\(" -#define CONVERTTOCPP_REGEX "(\\*?%?[a-zA-Z_][\\w\\.]*(?:\\[[^\\[^<^>]+\\])*)"\ - "(?:\\s+)=(?:\\s+)%CONVERTTOCPP\\[([^\\[]*)\\]\\(" +extern const char *CPP_ARG; +extern const char *CPP_ARG_REMOVED; +extern const char *CPP_RETURN_VAR; +extern const char *CPP_SELF_VAR; +extern const char *PYTHON_ARG; +extern const char *PYTHON_ARGS; +extern const char *PYTHON_OVERRIDE_VAR; +extern const char *PYTHON_RETURN_VAR; +extern const char *PYTHON_TO_CPP_VAR; +extern const char *SMART_POINTER_GETTER; + +extern const char *CONV_RULE_OUT_VAR_SUFFIX; +extern const char *BEGIN_ALLOW_THREADS; +extern const char *END_ALLOW_THREADS; #include <generator.h> @@ -71,61 +63,20 @@ class ShibokenGenerator : public Generator { public: ShibokenGenerator(); - virtual ~ShibokenGenerator(); + ~ShibokenGenerator() override; - QString translateTypeForWrapperMethod(const AbstractMetaType* cType, - const AbstractMetaClass* context, Options opt = NoOption) const; + const char *name() const override { return "Shiboken"; } /** - * Returns a map with all functions grouped, the function name is used as key. - * Example of return value: { "foo" -> ["foo(int)", "foo(int, long)], "bar" -> "bar(double)"} - * \param scope Where to search for functions, null means all global functions. - */ - QMap<QString, AbstractMetaFunctionList> getFunctionGroups(const AbstractMetaClass* scope = 0); - - /** - * Returns all different inherited overloads of func. - * The function can be called multiple times without duplication. - * \param func the metafunction to be searched in subclasses. - * \param seen the function's minimal signatures already seen. - */ - AbstractMetaFunctionList getInheritedOverloads(const AbstractMetaFunction *func, QSet<QString> *seen); + * Helper function to find for argument default value + */ + static QString getDefaultValue(const AbstractMetaFunction* func, const AbstractMetaArgument* arg); - /** - * Returns all different inherited overloads of func, and includes func as well. - * The function can be called multiple times without duplication. - * \param func the metafunction to be searched in subclasses. - * \param seen the function's minimal signatures already seen. - */ - AbstractMetaFunctionList getFunctionAndInheritedOverloads(const AbstractMetaFunction *func, QSet<QString> *seen); + /// Returns a list of all ancestor classes for the given class. + AbstractMetaClassList getAllAncestors(const AbstractMetaClass* metaClass) const; - /** - * Returns all overloads for a function named \p functionName. - * \param scope scope used to search for overloads. - * \param functionName the function name. - */ - AbstractMetaFunctionList getFunctionOverloads(const AbstractMetaClass* scope, const QString& functionName); - /** - * Write a function argument in the C++ in the text stream \p s. - * This function just call \code s << argumentString(); \endcode - * \param s text stream used to write the output. - * \param func the current metafunction. - * \param argument metaargument information to be parsed. - * \param options some extra options. - */ - void writeArgument(QTextStream &s, - const AbstractMetaFunction* func, - const AbstractMetaArgument* argument, - Options options = NoOption) const; - /** - * Create a QString in the C++ format to an function argument. - * \param func the current metafunction. - * \param argument metaargument information to be parsed. - * \param options some extra options. - */ - QString argumentString(const AbstractMetaFunction* func, - const AbstractMetaArgument* argument, - Options options = NoOption) const; +protected: + bool doSetup() override; void writeArgumentNames(QTextStream &s, const AbstractMetaFunction* func, @@ -141,14 +92,21 @@ public: void writeFunctionArguments(QTextStream &s, const AbstractMetaFunction* func, Options options = NoOption) const override; - QString functionReturnType(const AbstractMetaFunction* func, Options options = NoOption) const; - /// Utility function for writeCodeSnips. - typedef QPair<const AbstractMetaArgument*, QString> ArgumentVarReplacementPair; - typedef QVector<ArgumentVarReplacementPair> ArgumentVarReplacementList; - ArgumentVarReplacementList getArgumentReplacement(const AbstractMetaFunction* func, - bool usePyArgs, TypeSystem::Language language, - const AbstractMetaArgument* lastArg); + /** + * Returns a map with all functions grouped, the function name is used as key. + * Example of return value: { "foo" -> ["foo(int)", "foo(int, long)], "bar" -> "bar(double)"} + * \param scope Where to search for functions, null means all global functions. + */ + QMap<QString, AbstractMetaFunctionList> getFunctionGroups(const AbstractMetaClass* scope = 0); + + /** + * Returns all different inherited overloads of func, and includes func as well. + * The function can be called multiple times without duplication. + * \param func the metafunction to be searched in subclasses. + * \param seen the function's minimal signatures already seen. + */ + AbstractMetaFunctionList getFunctionAndInheritedOverloads(const AbstractMetaFunction *func, QSet<QString> *seen); /// Write user's custom code snippets at class or module level. void writeCodeSnips(QTextStream& s, @@ -164,33 +122,9 @@ public: const AbstractMetaFunction* func, const AbstractMetaArgument* lastArg = 0); - /// Returns a string with the user's custom code snippets that comply with \p position and \p language. - QString getCodeSnippets(const QVector<CodeSnip> & codeSnips, TypeSystem::CodeSnipPosition position, TypeSystem::Language language); - /// Replaces variables for the user's custom code at global or class level. void processCodeSnip(QString& code, const AbstractMetaClass* context = 0); - /// Replaces the %CONVERTTOPYTHON type system variable. - inline void replaceConvertToPythonTypeSystemVariable(QString& code) - { - replaceConverterTypeSystemVariable(TypeSystemToPythonFunction, code); - } - /// Replaces the %CONVERTTOCPP type system variable. - inline void replaceConvertToCppTypeSystemVariable(QString& code) - { - replaceConverterTypeSystemVariable(TypeSystemToCppFunction, code); - } - /// Replaces the %ISCONVERTIBLE type system variable. - inline void replaceIsConvertibleToCppTypeSystemVariable(QString& code) - { - replaceConverterTypeSystemVariable(TypeSystemIsConvertibleFunction, code); - } - /// Replaces the %CHECKTYPE type system variable. - inline void replaceTypeCheckTypeSystemVariable(QString& code) - { - replaceConverterTypeSystemVariable(TypeSystemCheckFunction, code); - } - /** * Verifies if any of the function's code injections of the "native" * type needs the type system variable "%PYSELF". @@ -239,8 +173,8 @@ public: * \param arg_count the number of function arguments */ QString functionSignature(const AbstractMetaFunction* func, - QString prepend = QString(), - QString append = QString(), + const QString &prepend = QString(), + const QString &append = QString(), Options options = NoOption, int arg_count = -1) const; @@ -259,9 +193,6 @@ public: /// Returns a list of parent classes for a given class. AbstractMetaClassList getBaseClasses(const AbstractMetaClass* metaClass) const; - /// Returns a list of all ancestor classes for the given class. - AbstractMetaClassList getAllAncestors(const AbstractMetaClass* metaClass) const; - const AbstractMetaClass* getMultipleInheritingClass(const AbstractMetaClass* metaClass); void writeToPythonConversion(QTextStream& s, const AbstractMetaType* type, @@ -283,7 +214,8 @@ public: QString wrapperName(const AbstractMetaClass* metaClass) const; QString wrapperName(const AbstractMetaType *metaType) const; - QString fullPythonFunctionName(const AbstractMetaFunction* func); + QString fullPythonClassName(const AbstractMetaClass *metaClass); + QString fullPythonFunctionName(const AbstractMetaFunction *func); //WS static QString protectedEnumSurrogateName(const AbstractMetaEnum* metaEnum); static QString protectedFieldGetterName(const AbstractMetaField* field); @@ -292,16 +224,16 @@ public: static QString pythonPrimitiveTypeName(const QString& cppTypeName); static QString pythonPrimitiveTypeName(const PrimitiveTypeEntry* type); - static QString pythonOperatorFunctionName(QString cppOpFuncName); + static QString pythonOperatorFunctionName(const QString &cppOpFuncName); static QString pythonOperatorFunctionName(const AbstractMetaFunction* func); - static QString pythonRichCompareOperatorId(QString cppOpFuncName); + static QString pythonRichCompareOperatorId(const QString &cppOpFuncName); static QString pythonRichCompareOperatorId(const AbstractMetaFunction* func); static QString fixedCppTypeName(const CustomConversion::TargetToNativeConversion* toNative); static QString fixedCppTypeName(const AbstractMetaType* type); static QString fixedCppTypeName(const TypeEntry* type, QString typeName = QString()); - static bool isNumber(QString cpythonApiName); + static bool isNumber(const QString &cpythonApiName); static bool isNumber(const TypeEntry* type); static bool isNumber(const AbstractMetaType* type); static bool isPyInt(const TypeEntry* type); @@ -389,9 +321,10 @@ public: QString cpythonSetattroFunctionName(const AbstractMetaClass* metaClass); QString cpythonGetterFunctionName(const AbstractMetaField* metaField); QString cpythonSetterFunctionName(const AbstractMetaField* metaField); - QString cpythonWrapperCPtr(const AbstractMetaClass* metaClass, QString argName = QLatin1String(PYTHON_SELF_VAR)); - QString cpythonWrapperCPtr(const AbstractMetaType *metaType, QString argName); - QString cpythonWrapperCPtr(const TypeEntry* type, QString argName); + QString cpythonWrapperCPtr(const AbstractMetaClass* metaClass, + const QString &argName = QLatin1String("self")); + QString cpythonWrapperCPtr(const AbstractMetaType *metaType, const QString &argName); + QString cpythonWrapperCPtr(const TypeEntry* type, const QString &argName); /// Guesses the scope to where belongs an argument's default value. QString guessScopeForDefaultValue(const AbstractMetaFunction *func, @@ -414,6 +347,7 @@ public: QString getModuleHeaderFileName(const QString& moduleName = QString()) const; OptionDescriptions options() const override; + bool handleOption(const QString &key, const QString &value) override; /// Returns true if the user enabled the so called "parent constructor heuristic". bool useCtorHeuristic() const; @@ -426,6 +360,7 @@ public: /// Returns true if the generated code should use the "#define protected public" hack. bool avoidProtectedHack() const; QString cppApiVariableName(const QString& moduleName = QString()) const; + QString pythonModuleObjectName(const QString& moduleName = QString()) const; QString convertersVariableName(const QString& moduleName = QString()) const; /** * Returns the type index variable name for a given class. If \p alternativeTemplateName is true @@ -457,26 +392,12 @@ public: void writeMinimalConstructorExpression(QTextStream& s, const AbstractMetaType* type, const QString& defaultCtor = QString()); void writeMinimalConstructorExpression(QTextStream& s, const TypeEntry* type, const QString& defaultCtor = QString()); - /** - * Helper function to find for argument default value - */ - static QString getDefaultValue(const AbstractMetaFunction* func, const AbstractMetaArgument* arg); -protected: - bool doSetup(const QMap<QString, QString>& args); void collectContainerTypesFromConverterMacros(const QString& code, bool toPythonMacro); // verify whether the class is copyable bool isCopyable(const AbstractMetaClass* metaClass); - bool m_native_jump_table; - static QHash<QString, QString> m_pythonPrimitiveTypeName; - static QHash<QString, QString> m_pythonOperators; - static QHash<QString, QString> m_formatUnits; - static QHash<QString, QString> m_tpFuncs; - static QStringList m_knownPythonTypes; - void clearTpFuncs(); - const char* name() const { return "Shiboken"; } /// Initializes correspondences between primitive and Python types. static void initPrimitiveTypesCorrespondences(); @@ -505,6 +426,74 @@ protected: Indentor INDENT; + const QRegularExpression &convertToCppRegEx() const + { return m_typeSystemConvRegEx[TypeSystemToCppFunction]; } + + static QString pythonArgsAt(int i); + + static QHash<QString, QString> m_pythonPrimitiveTypeName; + static QHash<QString, QString> m_pythonOperators; + static QHash<QString, QString> m_formatUnits; + static QHash<QString, QString> m_tpFuncs; + static QStringList m_knownPythonTypes; + +private: + QString translateTypeForWrapperMethod(const AbstractMetaType* cType, + const AbstractMetaClass* context, + Options opt = NoOption) const; + + /** + * Returns all different inherited overloads of func. + * The function can be called multiple times without duplication. + * \param func the metafunction to be searched in subclasses. + * \param seen the function's minimal signatures already seen. + */ + AbstractMetaFunctionList getInheritedOverloads(const AbstractMetaFunction *func, + QSet<QString> *seen); + + /** + * Returns all overloads for a function named \p functionName. + * \param scope scope used to search for overloads. + * \param functionName the function name. + */ + AbstractMetaFunctionList getFunctionOverloads(const AbstractMetaClass* scope, + const QString& functionName); + /** + * Write a function argument in the C++ in the text stream \p s. + * This function just call \code s << argumentString(); \endcode + * \param s text stream used to write the output. + * \param func the current metafunction. + * \param argument metaargument information to be parsed. + * \param options some extra options. + */ + void writeArgument(QTextStream &s, + const AbstractMetaFunction* func, + const AbstractMetaArgument* argument, + Options options = NoOption) const; + /** + * Create a QString in the C++ format to an function argument. + * \param func the current metafunction. + * \param argument metaargument information to be parsed. + * \param options some extra options. + */ + QString argumentString(const AbstractMetaFunction* func, + const AbstractMetaArgument* argument, + Options options = NoOption) const; + + QString functionReturnType(const AbstractMetaFunction* func, Options options = NoOption) const; + + /// Utility function for writeCodeSnips. + typedef QPair<const AbstractMetaArgument*, QString> ArgumentVarReplacementPair; + typedef QVector<ArgumentVarReplacementPair> ArgumentVarReplacementList; + ArgumentVarReplacementList getArgumentReplacement(const AbstractMetaFunction* func, + bool usePyArgs, TypeSystem::Language language, + const AbstractMetaArgument* lastArg); + + /// Returns a string with the user's custom code snippets that comply with \p position and \p language. + QString getCodeSnippets(const QVector<CodeSnip> & codeSnips, + TypeSystem::CodeSnipPosition position, + TypeSystem::Language language); + enum TypeSystemConverterVariable { TypeSystemCheckFunction = 0, TypeSystemIsConvertibleFunction, @@ -514,15 +503,36 @@ protected: }; void replaceConverterTypeSystemVariable(TypeSystemConverterVariable converterVariable, QString& code); - static QString msgCouldNotFindMinimalConstructor(const QString &where, const QString &type); + /// Replaces the %CONVERTTOPYTHON type system variable. + inline void replaceConvertToPythonTypeSystemVariable(QString& code) + { + replaceConverterTypeSystemVariable(TypeSystemToPythonFunction, code); + } + /// Replaces the %CONVERTTOCPP type system variable. + inline void replaceConvertToCppTypeSystemVariable(QString& code) + { + replaceConverterTypeSystemVariable(TypeSystemToCppFunction, code); + } + /// Replaces the %ISCONVERTIBLE type system variable. + inline void replaceIsConvertibleToCppTypeSystemVariable(QString& code) + { + replaceConverterTypeSystemVariable(TypeSystemIsConvertibleFunction, code); + } + /// Replaces the %CHECKTYPE type system variable. + inline void replaceTypeCheckTypeSystemVariable(QString& code) + { + replaceConverterTypeSystemVariable(TypeSystemCheckFunction, code); + } -private: - bool m_useCtorHeuristic; - bool m_userReturnValueHeuristic; - bool m_usePySideExtensions; - bool m_verboseErrorMessagesDisabled; - bool m_useIsNullAsNbNonZero; - bool m_avoidProtectedHack; + /// Return a prefix with '_' suitable for names in C++ + QString moduleCppPrefix(const QString& moduleName = QString()) const; + + bool m_useCtorHeuristic = false; + bool m_userReturnValueHeuristic = false; + bool m_usePySideExtensions = false; + bool m_verboseErrorMessagesDisabled = false; + bool m_useIsNullAsNbNonZero = false; + bool m_avoidProtectedHack = false; typedef QHash<QString, AbstractMetaType*> AbstractMetaTypeCache; AbstractMetaTypeCache m_metaTypeFromStringCache; diff --git a/sources/shiboken2/libshiboken/autodecref.h b/sources/shiboken2/libshiboken/autodecref.h index 7b6aa47da..72fd236de 100644 --- a/sources/shiboken2/libshiboken/autodecref.h +++ b/sources/shiboken2/libshiboken/autodecref.h @@ -43,11 +43,6 @@ #include "sbkpython.h" #include "basewrapper.h" -#ifdef _MSC_VER -__pragma(warning(push)) -__pragma(warning(disable:4522)) // warning: C4522: 'Shiboken::AutoDecRef': multiple assignment operators specified -#endif - struct SbkObject; namespace Shiboken { @@ -58,6 +53,11 @@ namespace Shiboken struct LIBSHIBOKEN_API AutoDecRef { public: + AutoDecRef(const AutoDecRef&) = delete; + AutoDecRef(AutoDecRef&&) = delete; + AutoDecRef& operator=(const AutoDecRef&) = delete; + AutoDecRef& operator=(AutoDecRef&&) = delete; + /** * AutoDecRef constructor. * \param pyobj A borrowed reference to a Python object @@ -92,35 +92,18 @@ public: } /** - * Decref the current borrowed python reference and take the reference - * borrowed by \p other, so other.isNull() will return true. - */ - void operator=(AutoDecRef& other) - { - Py_XDECREF(m_pyObj); - m_pyObj = other.m_pyObj; - other.m_pyObj = 0; - } - - /** * Decref the current borrowed python reference and borrow \p other. */ - void operator=(PyObject* other) + void reset(PyObject* other) { Py_XDECREF(m_pyObj); m_pyObj = other; } private: PyObject* m_pyObj; - AutoDecRef(const AutoDecRef&); - AutoDecRef& operator=(const AutoDecRef&); }; } // namespace Shiboken -#ifdef _MSC_VER -__pragma(warning(pop)) -#endif - #endif // AUTODECREF_H diff --git a/sources/shiboken2/libshiboken/basewrapper.cpp b/sources/shiboken2/libshiboken/basewrapper.cpp index 5ca4172c8..c9e3b9d1b 100644 --- a/sources/shiboken2/libshiboken/basewrapper.cpp +++ b/sources/shiboken2/libshiboken/basewrapper.cpp @@ -40,6 +40,7 @@ #include "basewrapper.h" #include "basewrapper_p.h" #include "bindingmanager.h" +#include "helper.h" #include "sbkconverter.h" #include "sbkenum.h" #include "sbkstring.h" @@ -60,6 +61,15 @@ namespace { void _destroyParentInfo(SbkObject* obj, bool keepReference); } +static void callDestructor(const Shiboken::DtorAccumulatorVisitor::DestructorEntries &dts) +{ + for (const auto &e : dts) { + Shiboken::ThreadStateSaver threadSaver; + threadSaver.save(); + e.destructor(e.cppInstance); + } +} + extern "C" { @@ -118,20 +128,15 @@ static int SbkObject_traverse(PyObject* self, visitproc visit, void* arg) //Visit children Shiboken::ParentInfo* pInfo = sbkSelf->d->parentInfo; if (pInfo) { - std::set<SbkObject*>::const_iterator it = pInfo->children.begin(); - for(; it != pInfo->children.end(); ++it) - Py_VISIT(*it); + for (SbkObject *c : pInfo->children) + Py_VISIT(c); } //Visit refs Shiboken::RefCountMap* rInfo = sbkSelf->d->referredObjects; if (rInfo) { - Shiboken::RefCountMap::const_iterator it = rInfo->begin(); - for (; it != rInfo->end(); ++it) { - std::list<PyObject*>::const_iterator ref = it->second.begin(); - for(; ref != it->second.end(); ++ref) - Py_VISIT(*ref); - } + for (auto it = rInfo->begin(), end = rInfo->end(); it != end; ++it) + Py_VISIT(it->second); } if (sbkSelf->ob_dict) @@ -186,6 +191,12 @@ SbkObjectType *SbkObject_TypeF(void) return reinterpret_cast<SbkObjectType *>(type); } +static int mainThreadDeletionHandler(void *) +{ + if (Py_IsInitialized()) + Shiboken::BindingManager::instance().runDeletionInMainThread(); + return 0; +} static void SbkDeallocWrapperCommon(PyObject* pyObj, bool canDelete) { @@ -214,11 +225,32 @@ static void SbkDeallocWrapperCommon(PyObject* pyObj, bool canDelete) PyObject_ClearWeakRefs(pyObj); // If I have ownership and is valid delete C++ pointer - if (canDelete && sbkObj->d->hasOwnership && sbkObj->d->validCppObject) { - SbkObjectTypePrivate *sotp = PepType_SOTP(pyType); + SbkObjectTypePrivate *sotp{nullptr}; + canDelete &= sbkObj->d->hasOwnership && sbkObj->d->validCppObject; + if (canDelete) { + sotp = PepType_SOTP(pyType); + if (sotp->delete_in_main_thread && Shiboken::currentThreadId() != Shiboken::mainThreadId()) { + auto &bindingManager = Shiboken::BindingManager::instance(); + if (sotp->is_multicpp) { + Shiboken::DtorAccumulatorVisitor visitor(sbkObj); + Shiboken::walkThroughClassHierarchy(Py_TYPE(pyObj), &visitor); + for (const auto &e : visitor.entries()) + bindingManager.addToDeletionInMainThread(e); + } else { + Shiboken::DestructorEntry e{sotp->cpp_dtor, sbkObj->d->cptr[0]}; + bindingManager.addToDeletionInMainThread(e); + } + Py_AddPendingCall(mainThreadDeletionHandler, nullptr); + canDelete = false; + } + } + + if (canDelete) { if (sotp->is_multicpp) { - Shiboken::DeallocVisitor visitor(sbkObj); + Shiboken::DtorAccumulatorVisitor visitor(sbkObj); Shiboken::walkThroughClassHierarchy(Py_TYPE(pyObj), &visitor); + Shiboken::Object::deallocData(sbkObj, true); + callDestructor(visitor.entries()); } else { void* cptr = sbkObj->d->cptr[0]; Shiboken::Object::deallocData(sbkObj, true); @@ -325,7 +357,7 @@ PyObject* SbkObjectTypeTpNew(PyTypeObject* metatype, PyObject* args, PyObject* k Shiboken::ObjectType::initPrivateData(newType); SbkObjectTypePrivate *sotp = PepType_SOTP(newType); - std::list<SbkObjectType*> bases = Shiboken::getCppBaseClasses(reinterpret_cast<PyTypeObject*>(newType)); + const auto bases = Shiboken::getCppBaseClasses(reinterpret_cast<PyTypeObject*>(newType)); if (bases.size() == 1) { SbkObjectTypePrivate *parentType = PepType_SOTP(bases.front()); sotp->mi_offsets = parentType->mi_offsets; @@ -352,10 +384,9 @@ PyObject* SbkObjectTypeTpNew(PyTypeObject* metatype, PyObject* args, PyObject* k sotp->d_func = nullptr; sotp->is_user_type = 1; - std::list<SbkObjectType*>::const_iterator it = bases.begin(); - for (; it != bases.end(); ++it) { - if (PepType_SOTP(*it)->subtype_init) - PepType_SOTP(*it)->subtype_init(newType, args, kwds); + for (SbkObjectType *base : bases) { + if (PepType_SOTP(base)->subtype_init) + PepType_SOTP(base)->subtype_init(newType, args, kwds); } return reinterpret_cast<PyObject*>(newType); @@ -455,37 +486,22 @@ void _destroyParentInfo(SbkObject* obj, bool keepReference) namespace Shiboken { - -static void decRefPyObjectList(const std::list<PyObject*> &pyObj, PyObject* skip = 0); - -static void _walkThroughClassHierarchy(PyTypeObject* currentType, HierarchyVisitor* visitor) +bool walkThroughClassHierarchy(PyTypeObject *currentType, HierarchyVisitor *visitor) { PyObject* bases = currentType->tp_bases; Py_ssize_t numBases = PyTuple_GET_SIZE(bases); - for (int i = 0; i < numBases; ++i) { + bool result = false; + for (int i = 0; !result && i < numBases; ++i) { PyTypeObject* type = reinterpret_cast<PyTypeObject*>(PyTuple_GET_ITEM(bases, i)); - - if (!PyType_IsSubtype(type, reinterpret_cast<PyTypeObject*>(SbkObject_TypeF()))) { - continue; - } else { + if (PyType_IsSubtype(type, reinterpret_cast<PyTypeObject*>(SbkObject_TypeF()))) { SbkObjectType* sbkType = reinterpret_cast<SbkObjectType*>(type); - if (PepType_SOTP(sbkType)->is_user_type) - _walkThroughClassHierarchy(type, visitor); - else - visitor->visit(sbkType); + result = PepType_SOTP(sbkType)->is_user_type + ? walkThroughClassHierarchy(type, visitor) : visitor->visit(sbkType); } - if (visitor->wasFinished()) - break; } + return result; } -void walkThroughClassHierarchy(PyTypeObject* currentType, HierarchyVisitor* visitor) -{ - _walkThroughClassHierarchy(currentType, visitor); - visitor->done(); -} - - bool importModule(const char* moduleName, PyTypeObject*** cppApiPtr) { PyObject* sysModules = PyImport_GetModuleDict(); @@ -517,27 +533,36 @@ bool importModule(const char* moduleName, PyTypeObject*** cppApiPtr) // Wrapper metatype and base type ---------------------------------------------------------- -void DtorCallerVisitor::visit(SbkObjectType* node) +HierarchyVisitor::HierarchyVisitor() = default; +HierarchyVisitor::~HierarchyVisitor() = default; + +bool BaseCountVisitor::visit(SbkObjectType *) { - m_ptrs.push_back(std::make_pair(m_pyObj->d->cptr[m_ptrs.size()], node)); + m_count++; + return false; } -void DtorCallerVisitor::done() +bool BaseAccumulatorVisitor::visit(SbkObjectType *node) { - std::list<std::pair<void*, SbkObjectType*> >::const_iterator it = m_ptrs.begin(); - for (; it != m_ptrs.end(); ++it) { - Shiboken::ThreadStateSaver threadSaver; - threadSaver.save(); - PepType_SOTP(it->second)->cpp_dtor(it->first); - } + m_bases.push_back(node); + return false; } -void DeallocVisitor::done() +bool GetIndexVisitor::visit(SbkObjectType *node) { - Shiboken::Object::deallocData(m_pyObj, true); - DtorCallerVisitor::done(); + m_index++; + return PyType_IsSubtype(reinterpret_cast<PyTypeObject*>(node), m_desiredType); } +bool DtorAccumulatorVisitor::visit(SbkObjectType *node) +{ + m_entries.push_back(DestructorEntry{PepType_SOTP(node)->cpp_dtor, + m_pyObject->d->cptr[m_entries.size()]}); + return false; +} + +void _initMainThreadId(); // helper.cpp + namespace Conversions { void init(); } void init() @@ -546,6 +571,8 @@ void init() if (shibokenAlreadInitialised) return; + _initMainThreadId(); + Conversions::init(); PyEval_InitThreads(); @@ -609,25 +636,21 @@ void setErrorAboutWrongArguments(PyObject* args, const char* funcName, const cha class FindBaseTypeVisitor : public HierarchyVisitor { - public: - FindBaseTypeVisitor(PyTypeObject* typeToFind) : m_found(false), m_typeToFind(typeToFind) {} - virtual void visit(SbkObjectType* node) - { - if (reinterpret_cast<PyTypeObject*>(node) == m_typeToFind) { - m_found = true; - finish(); - } - } - bool found() const { return m_found; } +public: + explicit FindBaseTypeVisitor(PyTypeObject *typeToFind) : m_typeToFind(typeToFind) {} + + bool visit(SbkObjectType *node) override + { + return reinterpret_cast<PyTypeObject*>(node) == m_typeToFind; + } - private: - bool m_found; - PyTypeObject* m_typeToFind; +private: + PyTypeObject *m_typeToFind; }; -std::list<SbkObject*> splitPyObject(PyObject* pyObj) +std::vector<SbkObject *> splitPyObject(PyObject* pyObj) { - std::list<SbkObject*> result; + std::vector<SbkObject *> result; if (PySequence_Check(pyObj)) { AutoDecRef lst(PySequence_Fast(pyObj, "Invalid keep reference object.")); if (!lst.isNull()) { @@ -643,14 +666,11 @@ std::list<SbkObject*> splitPyObject(PyObject* pyObj) return result; } -static void decRefPyObjectList(const std::list<PyObject*>& lst, PyObject *skip) +template <class Iterator> +inline void decRefPyObjectList(Iterator i1, Iterator i2) { - std::list<PyObject*>::const_iterator iter = lst.begin(); - while(iter != lst.end()) { - if (*iter != skip) - Py_DECREF(*iter); - ++iter; - } + for (; i1 != i2; ++i1) + Py_DECREF(i1->second); } namespace ObjectType @@ -669,8 +689,7 @@ bool isUserType(PyTypeObject* type) bool canCallConstructor(PyTypeObject* myType, PyTypeObject* ctorType) { FindBaseTypeVisitor visitor(ctorType); - walkThroughClassHierarchy(myType, &visitor); - if (!visitor.found()) { + if (!walkThroughClassHierarchy(myType, &visitor)) { PyErr_Format(PyExc_TypeError, "%s isn't a direct base class of %s", ctorType->tp_name, myType->tp_name); return false; } @@ -748,7 +767,7 @@ introduceWrapperType(PyObject *enclosingObject, ObjectDestructor cppObjDtor, SbkObjectType *baseType, PyObject *baseTypes, - bool isInnerClass) + unsigned wrapperFlags) { if (baseType) { typeSpec->slots[0].pfunc = reinterpret_cast<void *>(baseType); @@ -773,10 +792,14 @@ introduceWrapperType(PyObject *enclosingObject, return nullptr; initPrivateData(type); + auto sotp = PepType_SOTP(type); + if (wrapperFlags & DeleteInMainThread) + sotp->delete_in_main_thread = 1; + setOriginalName(type, originalName); setDestructorFunction(type, cppObjDtor); - if (isInnerClass) { + if (wrapperFlags & InnerClass) { if (PyDict_SetItemString(enclosingObject, typeName, reinterpret_cast<PyObject *>(type)) == 0) return type; else @@ -844,12 +867,13 @@ static void setSequenceOwnership(PyObject* pyObj, bool owner) if (PySequence_Check(pyObj) && has_length) { Py_ssize_t size = PySequence_Size(pyObj); if (size > 0) { - std::list<SbkObject*> objs = splitPyObject(pyObj); - for (auto it = objs.begin(), end = objs.end(); it != end; ++it) { - if (owner) - getOwnership(*it); - else - releaseOwnership(*it); + const auto objs = splitPyObject(pyObj); + if (owner) { + for (SbkObject *o : objs) + getOwnership(o); + } else { + for (SbkObject *o : objs) + releaseOwnership(o); } } } else if (Object::checkType(pyObj)) { @@ -885,8 +909,9 @@ void callCppDestructors(SbkObject* pyObj) PyTypeObject *type = Py_TYPE(pyObj); SbkObjectTypePrivate * sotp = PepType_SOTP(type); if (sotp->is_multicpp) { - Shiboken::DtorCallerVisitor visitor(pyObj); + Shiboken::DtorAccumulatorVisitor visitor(pyObj); Shiboken::walkThroughClassHierarchy(type, &visitor); + callDestructor(visitor.entries()); } else { Shiboken::ThreadStateSaver threadSaver; threadSaver.save(); @@ -977,10 +1002,9 @@ void invalidate(SbkObject* self) static void recursive_invalidate(PyObject* pyobj, std::set<SbkObject*>& seen) { - std::list<SbkObject*> objs = splitPyObject(pyobj); - std::list<SbkObject*>::const_iterator it = objs.begin(); - for (; it != objs.end(); it++) - recursive_invalidate(*it, seen); + const auto objs = splitPyObject(pyobj); + for (SbkObject *o : objs) + recursive_invalidate(o, seen); } static void recursive_invalidate(SbkObject* self, std::set<SbkObject*>& seen) @@ -999,30 +1023,22 @@ static void recursive_invalidate(SbkObject* self, std::set<SbkObject*>& seen) if (self->d->parentInfo) { // Create a copy because this list can be changed during the process ChildrenList copy = self->d->parentInfo->children; - ChildrenList::iterator it = copy.begin(); - for (; it != copy.end(); ++it) { + for (SbkObject *child : copy) { // invalidate the child - recursive_invalidate(*it, seen); + recursive_invalidate(child, seen); // if the parent not is a wrapper class, then remove children from him, because We do not know when this object will be destroyed if (!self->d->validCppObject) - removeParent(*it, true, true); + removeParent(child, true, true); } } // If has ref to other objects invalidate all if (self->d->referredObjects) { RefCountMap& refCountMap = *(self->d->referredObjects); - RefCountMap::iterator iter; - for (iter = refCountMap.begin(); iter != refCountMap.end(); ++iter) { - const std::list<PyObject*> lst = iter->second; - std::list<PyObject*>::const_iterator it = lst.begin(); - while(it != lst.end()) { - recursive_invalidate(*it, seen); - ++it; - } - } + for (auto it = refCountMap.begin(), end = refCountMap.end(); it != end; ++it) + recursive_invalidate(it->second, seen); } } @@ -1037,23 +1053,17 @@ void makeValid(SbkObject* self) // If it is a parent make all children valid if (self->d->parentInfo) { - ChildrenList::iterator it = self->d->parentInfo->children.begin(); - for (; it != self->d->parentInfo->children.end(); ++it) - makeValid(*it); + for (SbkObject *child : self->d->parentInfo->children) + makeValid(child); } // If has ref to other objects make all valid again if (self->d->referredObjects) { RefCountMap& refCountMap = *(self->d->referredObjects); RefCountMap::iterator iter; - for (iter = refCountMap.begin(); iter != refCountMap.end(); ++iter) { - const std::list<PyObject*> lst = iter->second; - std::list<PyObject*>::const_iterator it = lst.begin(); - while(it != lst.end()) { - if (Shiboken::Object::checkType(*it)) - makeValid(reinterpret_cast<SbkObject*>(*it)); - ++it; - } + for (auto it = refCountMap.begin(), end = refCountMap.end(); it != end; ++it) { + if (Shiboken::Object::checkType(it->second)) + makeValid(reinterpret_cast<SbkObject *>(it->second)); } } } @@ -1168,15 +1178,14 @@ SbkObject *findColocatedChild(SbkObject *wrapper, ChildrenList& children = pInfo->children; - ChildrenList::iterator childrenEnd = children.end(); - for (ChildrenList::iterator iChild = children.begin(); iChild != childrenEnd; ++iChild) { - if (!((*iChild)->d && (*iChild)->d->cptr)) + for (SbkObject *child : children) { + if (!(child->d && child->d->cptr)) continue; - if ((*iChild)->d->cptr[0] == wrapper->d->cptr[0]) { - if (reinterpret_cast<const void *>(Py_TYPE(*iChild)) == reinterpret_cast<const void *>(instanceType)) - return const_cast<SbkObject *>((*iChild)); + if (child->d->cptr[0] == wrapper->d->cptr[0]) { + if (reinterpret_cast<const void *>(Py_TYPE(child)) == reinterpret_cast<const void *>(instanceType)) + return child; else - return findColocatedChild(const_cast<SbkObject *>(*iChild), instanceType); + return findColocatedChild(child, instanceType); } } return 0; @@ -1435,57 +1444,56 @@ void* getTypeUserData(SbkObject* wrapper) return PepType_SOTP(Py_TYPE(wrapper))->user_data; } -void keepReference(SbkObject* self, const char* key, PyObject* referredObject, bool append) +static inline bool isNone(const PyObject *o) { - bool isNone = (!referredObject || (referredObject == Py_None)); - - if (!self->d->referredObjects) - self->d->referredObjects = new Shiboken::RefCountMap; - - RefCountMap& refCountMap = *(self->d->referredObjects); - RefCountMap::iterator iter = refCountMap.find(key); - std::list<PyObject*> objects; - if (iter != refCountMap.end()) { - objects = (*iter).second; - std::list<PyObject*>::const_iterator found = std::find(objects.begin(), objects.end(), referredObject); - - // skip if objects already exists - if (found != objects.end()) - return; - } + return o == nullptr || o == Py_None; +} - if (append && !isNone) { - refCountMap[key].push_back(referredObject); - Py_INCREF(referredObject); - } else if (!append) { - if (objects.size() > 0) - decRefPyObjectList(objects, isNone ? 0 : referredObject); - if (isNone) { - if (iter != refCountMap.end()) - refCountMap.erase(iter); - } else { - objects.clear(); - objects.push_back(referredObject); - refCountMap[key] = objects; - Py_INCREF(referredObject); +static void removeRefCountKey(SbkObject* self, const char *key) +{ + if (self->d->referredObjects) { + const auto iterPair = self->d->referredObjects->equal_range(key); + if (iterPair.first != iterPair.second) { + decRefPyObjectList(iterPair.first, iterPair.second); + self->d->referredObjects->erase(iterPair.first, iterPair.second); } } } -void removeReference(SbkObject* self, const char* key, PyObject* referredObject) +void keepReference(SbkObject* self, const char* key, PyObject* referredObject, bool append) { - if (!referredObject || (referredObject == Py_None)) + if (isNone(referredObject)) { + removeRefCountKey(self, key); return; + } - if (!self->d->referredObjects) + if (!self->d->referredObjects) { + self->d->referredObjects = + new Shiboken::RefCountMap{RefCountMap::value_type{key, referredObject}}; + Py_INCREF(referredObject); return; + } RefCountMap& refCountMap = *(self->d->referredObjects); - RefCountMap::iterator iter = refCountMap.find(key); - if (iter != refCountMap.end()) { - decRefPyObjectList(iter->second); - refCountMap.erase(iter); + const auto iterPair = refCountMap.equal_range(key); + if (std::any_of(iterPair.first, iterPair.second, + [referredObject](const RefCountMap::value_type &v) { return v.second == referredObject; })) { + return; + } + + if (!append && iterPair.first != iterPair.second) { + decRefPyObjectList(iterPair.first, iterPair.second); + refCountMap.erase(iterPair.first, iterPair.second); } + + refCountMap.insert(RefCountMap::value_type{key, referredObject}); + Py_INCREF(referredObject); +} + +void removeReference(SbkObject* self, const char* key, PyObject* referredObject) +{ + if (!isNone(referredObject)) + removeRefCountKey(self, key); } void clearReferences(SbkObject* self) @@ -1494,27 +1502,27 @@ void clearReferences(SbkObject* self) return; RefCountMap& refCountMap = *(self->d->referredObjects); - RefCountMap::iterator iter; - for (iter = refCountMap.begin(); iter != refCountMap.end(); ++iter) - decRefPyObjectList(iter->second); + for (auto it = refCountMap.begin(), end = refCountMap.end(); it != end; ++it) + Py_DECREF(it->second); self->d->referredObjects->clear(); } std::string info(SbkObject* self) { std::ostringstream s; - std::list<SbkObjectType*> bases; if (self->d && self->d->cptr) { + std::vector<SbkObjectType *> bases; if (ObjectType::isUserType(Py_TYPE(self))) bases = getCppBaseClasses(Py_TYPE(self)); else bases.push_back(reinterpret_cast<SbkObjectType*>(Py_TYPE(self))); s << "C++ address....... "; - std::list<SbkObjectType*>::const_iterator it = bases.begin(); - for (int i = 0; it != bases.end(); ++it, ++i) - s << reinterpret_cast<PyTypeObject *>(*it)->tp_name << '/' << self->d->cptr[i] << ' '; + for (size_t i = 0, size = bases.size(); i < size; ++i) { + auto base = reinterpret_cast<PyTypeObject *>(bases[i]); + s << base->tp_name << '/' << self->d->cptr[i] << ' '; + } s << "\n"; } else { @@ -1535,9 +1543,8 @@ std::string info(SbkObject* self) if (self->d->parentInfo && !self->d->parentInfo->children.empty()) { s << "children.......... "; - ChildrenList& children = self->d->parentInfo->children; - for (ChildrenList::const_iterator it = children.begin(); it != children.end(); ++it) { - Shiboken::AutoDecRef child(PyObject_Str(reinterpret_cast<PyObject *>(*it))); + for (SbkObject *sbkChild : self->d->parentInfo->children) { + Shiboken::AutoDecRef child(PyObject_Str(reinterpret_cast<PyObject *>(sbkChild))); s << String::toCString(child) << ' '; } s << '\n'; @@ -1546,17 +1553,16 @@ std::string info(SbkObject* self) if (self->d->referredObjects && !self->d->referredObjects->empty()) { Shiboken::RefCountMap& map = *self->d->referredObjects; s << "referred objects.. "; - Shiboken::RefCountMap::const_iterator it = map.begin(); - for (; it != map.end(); ++it) { - if (it != map.begin()) - s << " "; - s << '"' << it->first << "\" => "; - std::list<PyObject*>::const_iterator j = it->second.begin(); - for (; j != it->second.end(); ++j) { - Shiboken::AutoDecRef obj(PyObject_Str(*j)); - s << String::toCString(obj) << ' '; + std::string lastKey; + for (auto it = map.begin(), end = map.end(); it != end; ++it) { + if (it->first != lastKey) { + if (!lastKey.empty()) + s << " "; + s << '"' << it->first << "\" => "; + lastKey = it->first; } - s << ' '; + Shiboken::AutoDecRef obj(PyObject_Str(it->second)); + s << String::toCString(obj) << ' '; } s << '\n'; } diff --git a/sources/shiboken2/libshiboken/basewrapper.h b/sources/shiboken2/libshiboken/basewrapper.h index 134a3bc51..65849d783 100644 --- a/sources/shiboken2/libshiboken/basewrapper.h +++ b/sources/shiboken2/libshiboken/basewrapper.h @@ -190,6 +190,12 @@ LIBSHIBOKEN_API void setDestructorFunction(SbkObjectType* self, ObjectDes LIBSHIBOKEN_API void initPrivateData(SbkObjectType* self); +enum WrapperFlags +{ + InnerClass = 0x1, + DeleteInMainThread = 0x2 +}; + /** * Initializes a Shiboken wrapper type and adds it to the module, * or to the enclosing class if the type is an inner class. @@ -215,7 +221,7 @@ LIBSHIBOKEN_API SbkObjectType *introduceWrapperType(PyObject *enclosingObject, ObjectDestructor cppObjDtor, SbkObjectType *baseType, PyObject *baseTypes, - bool isInnerClass); + unsigned wrapperFlags = 0); /** * Set the subtype init hook for a type. diff --git a/sources/shiboken2/libshiboken/basewrapper_p.h b/sources/shiboken2/libshiboken/basewrapper_p.h index ebd2648e7..f8a381078 100644 --- a/sources/shiboken2/libshiboken/basewrapper_p.h +++ b/sources/shiboken2/libshiboken/basewrapper_p.h @@ -43,10 +43,10 @@ #include "sbkpython.h" #include "basewrapper.h" -#include <list> -#include <map> +#include <unordered_map> #include <set> #include <string> +#include <vector> struct SbkObject; struct SbkObjectType; @@ -58,7 +58,7 @@ namespace Shiboken * This mapping associates a method and argument of an wrapper object with the wrapper of * said argument when it needs the binding to help manage its reference count. */ -typedef std::map<std::string, std::list<PyObject*> > RefCountMap; +typedef std::unordered_multimap<std::string, PyObject *> RefCountMap; /// Linked list of SbkBaseWrapper pointers typedef std::set<SbkObject*> ChildrenList; @@ -137,6 +137,7 @@ struct SbkObjectTypePrivate /// Tells is the type is a value type or an object-type, see BEHAVIOUR_* constants. // TODO-CONVERTERS: to be deprecated/removed int type_behaviour : 2; + int delete_in_main_thread : 1; /// C++ name char* original_name; /// Type user data @@ -150,10 +151,21 @@ struct SbkObjectTypePrivate namespace Shiboken { + +/** + * \internal + * Data required to invoke a C++ destructor + */ +struct DestructorEntry +{ + ObjectDestructor destructor; + void *cppInstance; +}; + /** * Utility function used to transform a PyObject that implements sequence protocol into a std::list. **/ -std::list<SbkObject*> splitPyObject(PyObject* pyObj); +std::vector<SbkObject *> splitPyObject(PyObject* pyObj); /** * Visitor class used by walkOnClassHierarchy function. @@ -161,81 +173,71 @@ std::list<SbkObject*> splitPyObject(PyObject* pyObj); class HierarchyVisitor { public: - HierarchyVisitor() : m_wasFinished(false) {} - virtual ~HierarchyVisitor() {} - virtual void visit(SbkObjectType* node) = 0; - virtual void done() {} - void finish() { m_wasFinished = true; }; - bool wasFinished() const { return m_wasFinished; } -private: - bool m_wasFinished; + HierarchyVisitor(const HierarchyVisitor &) = delete; + HierarchyVisitor(HierarchyVisitor &&) = delete; + HierarchyVisitor &operator=(const HierarchyVisitor &) = delete; + HierarchyVisitor &operator=(HierarchyVisitor &&) = delete; + + HierarchyVisitor(); + virtual ~HierarchyVisitor(); + + virtual bool visit(SbkObjectType *node) = 0; // return true to terminate }; class BaseCountVisitor : public HierarchyVisitor { public: - BaseCountVisitor() : m_count(0) {} - - void visit(SbkObjectType*) - { - m_count++; - } + bool visit(SbkObjectType *) override; int count() const { return m_count; } + private: - int m_count; + int m_count = 0; }; class BaseAccumulatorVisitor : public HierarchyVisitor { public: - BaseAccumulatorVisitor() {} + typedef std::vector<SbkObjectType *> Result; - void visit(SbkObjectType* node) - { - m_bases.push_back(node); - } + bool visit(SbkObjectType *node) override; + + Result bases() const { return m_bases; } - std::list<SbkObjectType*> bases() const { return m_bases; } private: - std::list<SbkObjectType*> m_bases; + Result m_bases; }; class GetIndexVisitor : public HierarchyVisitor { public: - GetIndexVisitor(PyTypeObject* desiredType) : m_index(-1), m_desiredType(desiredType) {} - virtual void visit(SbkObjectType* node) - { - m_index++; - if (PyType_IsSubtype(reinterpret_cast<PyTypeObject*>(node), m_desiredType)) - finish(); - } + explicit GetIndexVisitor(PyTypeObject* desiredType) : m_desiredType(desiredType) {} + + bool visit(SbkObjectType *node) override; + int index() const { return m_index; } private: - int m_index; - PyTypeObject* m_desiredType; + int m_index = -1; + PyTypeObject *m_desiredType; }; -/// Call the destructor of each C++ object held by a Python object -class DtorCallerVisitor : public HierarchyVisitor +/// Collect destructors and C++ instances of each C++ object held by a Python +/// object +class DtorAccumulatorVisitor : public HierarchyVisitor { public: - DtorCallerVisitor(SbkObject* pyObj) : m_pyObj(pyObj) {} - void visit(SbkObjectType* node); - void done(); -protected: - std::list<std::pair<void*, SbkObjectType*> > m_ptrs; - SbkObject* m_pyObj; -}; + explicit DtorAccumulatorVisitor(SbkObject *pyObj) : m_pyObject(pyObj) {} -/// Dealloc of each C++ object held by a Python object, this implies a call to the C++ object destructor -class DeallocVisitor : public DtorCallerVisitor -{ -public: - DeallocVisitor(SbkObject* pyObj) : DtorCallerVisitor(pyObj) {} - void done(); + bool visit(SbkObjectType *node) override; + + using DestructorEntries = std::vector<DestructorEntry>; + + const DestructorEntries &entries() const { return m_entries; } + +private: + DestructorEntries m_entries; + SbkObject *m_pyObject; }; /// \internal Internal function used to walk on classes inheritance trees. @@ -244,7 +246,7 @@ public: * For each pure Shiboken type found, HiearchyVisitor::visit is called and the algorithm consider * all children of this type as visited. */ -void walkThroughClassHierarchy(PyTypeObject* currentType, HierarchyVisitor* visitor); +bool walkThroughClassHierarchy(PyTypeObject *currentType, HierarchyVisitor *visitor); inline int getTypeIndexOnHierarchy(PyTypeObject* baseType, PyTypeObject* desiredType) { @@ -260,7 +262,7 @@ inline int getNumberOfCppBaseClasses(PyTypeObject* baseType) return visitor.count(); } -inline std::list<SbkObjectType*> getCppBaseClasses(PyTypeObject* baseType) +inline std::vector<SbkObjectType *> getCppBaseClasses(PyTypeObject* baseType) { BaseAccumulatorVisitor visitor; walkThroughClassHierarchy(baseType, &visitor); diff --git a/sources/shiboken2/libshiboken/bindingmanager.cpp b/sources/shiboken2/libshiboken/bindingmanager.cpp index 0aa520b38..8a9e912fd 100644 --- a/sources/shiboken2/libshiboken/bindingmanager.cpp +++ b/sources/shiboken2/libshiboken/bindingmanager.cpp @@ -57,14 +57,12 @@ typedef std::unordered_map<const void *, SbkObject *> WrapperMap; class Graph { public: - typedef std::list<SbkObjectType*> NodeList; + typedef std::vector<SbkObjectType *> NodeList; typedef std::unordered_map<SbkObjectType *, NodeList> Edges; Edges m_edges; - Graph() - { - } + Graph() = default; void addEdge(SbkObjectType* from, SbkObjectType* to) { @@ -78,14 +76,13 @@ public: file << "digraph D {\n"; - Edges::const_iterator i = m_edges.begin(); - for (; i != m_edges.end(); ++i) { - SbkObjectType* node1 = i->first; + for (auto i = m_edges.begin(), end = m_edges.end(); i != end; ++i) { + auto node1 = reinterpret_cast<const PyTypeObject *>(i->first); const NodeList& nodeList = i->second; - NodeList::const_iterator j = nodeList.begin(); - for (; j != nodeList.end(); ++j) { - file << '"' << reinterpret_cast<PyTypeObject *>(*j)->tp_name << "\" -> \"" - << reinterpret_cast<PyTypeObject *>(node1)->tp_name << "\"\n"; + for (const SbkObjectType *o : nodeList) { + auto node2 = reinterpret_cast<const PyTypeObject *>(o); + file << '"' << node2->tp_name << "\" -> \"" + << node1->tp_name << "\"\n"; } } file << "}\n"; @@ -97,9 +94,8 @@ public: Edges::const_iterator edgesIt = m_edges.find(type); if (edgesIt != m_edges.end()) { const NodeList& adjNodes = m_edges.find(type)->second; - NodeList::const_iterator i = adjNodes.begin(); - for (; i != adjNodes.end(); ++i) { - SbkObjectType* newType = identifyType(cptr, *i, baseType); + for (SbkObjectType *node : adjNodes) { + SbkObjectType* newType = identifyType(cptr, node, baseType); if (newType) return newType; } @@ -115,9 +111,8 @@ public: if (typeFound != type) *cptr = typeFound; return type; - } else { - return nullptr; } + return nullptr; } }; @@ -128,10 +123,9 @@ static void showWrapperMap(const WrapperMap& wrapperMap) if (Py_VerboseFlag > 0) { fprintf(stderr, "-------------------------------\n"); fprintf(stderr, "WrapperMap: %p (size: %d)\n", &wrapperMap, (int) wrapperMap.size()); - WrapperMap::const_iterator iter; - for (iter = wrapperMap.begin(); iter != wrapperMap.end(); ++iter) { - const SbkObject *sbkObj = iter->second; - fprintf(stderr, "key: %p, value: %p (%s, refcnt: %d)\n", iter->first, + for (auto it = wrapperMap.begin(), end = wrapperMap.end(); it != end; ++it) { + const SbkObject *sbkObj = it->second; + fprintf(stderr, "key: %p, value: %p (%s, refcnt: %d)\n", it->first, static_cast<const void *>(sbkObj), (Py_TYPE(sbkObj))->tp_name, int(reinterpret_cast<const PyObject *>(sbkObj)->ob_refcnt)); @@ -142,8 +136,11 @@ static void showWrapperMap(const WrapperMap& wrapperMap) #endif struct BindingManager::BindingManagerPrivate { + using DestructorEntries = std::vector<DestructorEntry>; + WrapperMap wrapperMapper; Graph classHierarchy; + DestructorEntries deleteInMainThread; bool destroying; BindingManagerPrivate() : destroying(false) {} @@ -255,6 +252,18 @@ void BindingManager::releaseWrapper(SbkObject* sbkObj) sbkObj->d->validCppObject = false; } +void BindingManager::runDeletionInMainThread() +{ + for (const DestructorEntry &e : m_d->deleteInMainThread) + e.destructor(e.cppInstance); + m_d->deleteInMainThread.clear(); +} + +void BindingManager::addToDeletionInMainThread(const DestructorEntry &e) +{ + m_d->deleteInMainThread.push_back(e); +} + SbkObject* BindingManager::retrieveWrapper(const void* cptr) { WrapperMap::iterator iter = m_d->wrapperMapper.find(cptr); diff --git a/sources/shiboken2/libshiboken/bindingmanager.h b/sources/shiboken2/libshiboken/bindingmanager.h index 13a4d3a2e..d03aa999a 100644 --- a/sources/shiboken2/libshiboken/bindingmanager.h +++ b/sources/shiboken2/libshiboken/bindingmanager.h @@ -50,11 +50,18 @@ struct SbkObjectType; namespace Shiboken { +struct DestructorEntry; + typedef void (*ObjectVisitor)(SbkObject*, void*); class LIBSHIBOKEN_API BindingManager { public: + BindingManager(const BindingManager&) = delete; + BindingManager(BindingManager&&) = delete; + BindingManager& operator=(const BindingManager&) = delete; + BindingManager& operator=(BindingManager&&) = delete; + static BindingManager& instance(); bool hasWrapper(const void *cptr); @@ -62,6 +69,9 @@ public: void registerWrapper(SbkObject* pyObj, void* cptr); void releaseWrapper(SbkObject* wrapper); + void runDeletionInMainThread(); + void addToDeletionInMainThread(const DestructorEntry &); + SbkObject* retrieveWrapper(const void* cptr); PyObject* getOverride(const void* cptr, const char* methodName); @@ -94,10 +104,7 @@ public: private: ~BindingManager(); - // disable copy BindingManager(); - BindingManager(const BindingManager&); - BindingManager& operator=(const BindingManager&); struct BindingManagerPrivate; BindingManagerPrivate* m_d; diff --git a/sources/shiboken2/libshiboken/gilstate.h b/sources/shiboken2/libshiboken/gilstate.h index 00b049802..9da4871d1 100644 --- a/sources/shiboken2/libshiboken/gilstate.h +++ b/sources/shiboken2/libshiboken/gilstate.h @@ -49,6 +49,11 @@ namespace Shiboken class LIBSHIBOKEN_API GilState { public: + GilState(const GilState&) = delete; + GilState(GilState&&) = delete; + GilState& operator=(const GilState&) = delete; + GilState& operator=(GilState&&) = delete; + GilState(); ~GilState(); void release(); diff --git a/sources/shiboken2/libshiboken/helper.cpp b/sources/shiboken2/libshiboken/helper.cpp index 472924723..e42daff07 100644 --- a/sources/shiboken2/libshiboken/helper.cpp +++ b/sources/shiboken2/libshiboken/helper.cpp @@ -41,6 +41,12 @@ #include "sbkstring.h" #include <stdarg.h> +#ifdef _WIN32 +# include <windows.h> +#else +# include <pthread.h> +#endif + namespace Shiboken { @@ -141,4 +147,24 @@ int warning(PyObject* category, int stacklevel, const char* format, ...) return result; } +ThreadId currentThreadId() +{ +#if defined(_WIN32) + return GetCurrentThreadId(); +#elif defined(__APPLE_CC__) + return reinterpret_cast<ThreadId>(pthread_self()); +#else + return pthread_self(); +#endif +} + +// Internal, used by init() from main thread +static ThreadId _mainThreadId{0}; +void _initMainThreadId() { _mainThreadId = currentThreadId(); } + +ThreadId mainThreadId() +{ + return _mainThreadId; +} + } // namespace Shiboken diff --git a/sources/shiboken2/libshiboken/helper.h b/sources/shiboken2/libshiboken/helper.h index b215142c7..9b6d8903f 100644 --- a/sources/shiboken2/libshiboken/helper.h +++ b/sources/shiboken2/libshiboken/helper.h @@ -77,7 +77,12 @@ template<class T> class AutoArrayPointer { public: - AutoArrayPointer(int size) { data = new T[size]; } + AutoArrayPointer(const AutoArrayPointer&) = delete; + AutoArrayPointer(AutoArrayPointer&&) = delete; + AutoArrayPointer& operator=(const AutoArrayPointer&) = delete; + AutoArrayPointer& operator=(AutoArrayPointer&&) = delete; + + explicit AutoArrayPointer(int size) { data = new T[size]; } T& operator[](int pos) { return data[pos]; } operator T*() const { return data; } ~AutoArrayPointer() { delete[] data; } @@ -85,6 +90,10 @@ class AutoArrayPointer T* data; }; +typedef unsigned long long ThreadId; +LIBSHIBOKEN_API ThreadId currentThreadId(); +LIBSHIBOKEN_API ThreadId mainThreadId(); + /** * An utility function used to call PyErr_WarnEx with a formatted message. */ diff --git a/sources/shiboken2/libshiboken/pep384impl.cpp b/sources/shiboken2/libshiboken/pep384impl.cpp index 0baf6ed42..7cca03c84 100644 --- a/sources/shiboken2/libshiboken/pep384impl.cpp +++ b/sources/shiboken2/libshiboken/pep384impl.cpp @@ -38,7 +38,7 @@ ****************************************************************************/ #include "pep384impl.h" -#include <autodecref.h> +#include "autodecref.h" extern "C" { @@ -398,7 +398,10 @@ PyRun_String(const char *str, int start, PyObject *globals, PyObject *locals) return ret; } +#endif // Py_LIMITED_API + // This is only a simple local helper that returns a computed variable. +// Used also in Python 2. static PyObject * PepRun_GetResult(const char *command, const char *resvar) { @@ -415,6 +418,8 @@ PepRun_GetResult(const char *command, const char *resvar) return res; } +#ifdef Py_LIMITED_API + /***************************************************************************** * * Support for classobject.h @@ -497,15 +502,48 @@ static PyTypeObject *getFunctionType(void) PyTypeObject *PepStaticMethod_TypePtr = NULL; -static PyTypeObject *getStaticMethodType(void) +static PyTypeObject * +getStaticMethodType(void) { + // this works for Python 3, only + // "StaticMethodType = type(str.__dict__['maketrans'])\n"; static const char prog[] = - "StaticMethodType = type(str.__dict__['maketrans'])\n"; - return (PyTypeObject *) PepRun_GetResult(prog, "StaticMethodType"); + "from xxsubtype import spamlist\n" + "StaticMethod_Type = type(spamlist.__dict__['staticmeth'])\n"; + return (PyTypeObject *) PepRun_GetResult(prog, "StaticMethod_Type"); } +typedef struct { + PyObject_HEAD + PyObject *sm_callable; + PyObject *sm_dict; +} staticmethod; + +PyObject * +PyStaticMethod_New(PyObject *callable) +{ + staticmethod *sm = (staticmethod *) + PyType_GenericAlloc(PepStaticMethod_TypePtr, 0); + if (sm != NULL) { + Py_INCREF(callable); + sm->sm_callable = callable; + } + return (PyObject *)sm; +} #endif // Py_LIMITED_API +#if PY_VERSION_HEX < 0x03000000 +PyTypeObject *PepMethodDescr_TypePtr = NULL; + +static PyTypeObject * +getMethodDescrType(void) +{ + static const char prog[] = + "MethodDescr_Type = type(str.split)\n"; + return (PyTypeObject *) PepRun_GetResult(prog, "MethodDescr_Type"); +} +#endif + /***************************************************************************** * * Common newly needed functions @@ -630,6 +668,9 @@ Pep384_Init() PepFunction_TypePtr = getFunctionType(); PepStaticMethod_TypePtr = getStaticMethodType(); #endif +#if PY_VERSION_HEX < 0x03000000 + PepMethodDescr_TypePtr = getMethodDescrType(); +#endif } } // extern "C" diff --git a/sources/shiboken2/libshiboken/pep384impl.h b/sources/shiboken2/libshiboken/pep384impl.h index b566a6218..bfa8f38a7 100644 --- a/sources/shiboken2/libshiboken/pep384impl.h +++ b/sources/shiboken2/libshiboken/pep384impl.h @@ -63,6 +63,7 @@ extern "C" */ #ifdef Py_LIMITED_API // Why the hell is this useful debugging function not allowed? +// BTW: When used, it breaks on Windows, intentionally! LIBSHIBOKEN_API void _PyObject_Dump(PyObject *); #endif @@ -286,7 +287,7 @@ LIBSHIBOKEN_API PyObject *PyRun_String(const char *, int, PyObject *, PyObject * // But this is no problem as we check it's validity for every version. #define PYTHON_BUFFER_VERSION_COMPATIBLE (PY_VERSION_HEX >= 0x03030000 && \ - PY_VERSION_HEX < 0X0307FFFF) + PY_VERSION_HEX < 0x0307FFFF) #if !PYTHON_BUFFER_VERSION_COMPATIBLE # error Please check the buffer compatibility for this python version! #endif @@ -332,10 +333,11 @@ LIBSHIBOKEN_API PyObject *PepFunction_Get(PyObject *, const char *); #define PyFunction_Check(op) (Py_TYPE(op) == PepFunction_TypePtr) #define PyFunction_GET_CODE(func) PyFunction_GetCode(func) -#define PyFunction_GetCode(func) PepFunction_Get((PyObject *)func, "__code__") -#define PepFunction_GetName(func) PepFunction_Get((PyObject *)func, "__name__") +#define PyFunction_GetCode(func) PepFunction_Get((PyObject *)func, "__code__") +#define PepFunction_GetName(func) PepFunction_Get((PyObject *)func, "__name__") #else -#define PepFunction_GetName(func) (((PyFunctionObject *)func)->func_name) +#define PepFunction_TypePtr (&PyFunction_Type) +#define PepFunction_GetName(func) (((PyFunctionObject *)func)->func_name) #endif /***************************************************************************** @@ -467,9 +469,16 @@ LIBSHIBOKEN_API PyObject *_Pep_PrivateMangle(PyObject *self, PyObject *name); #ifdef Py_LIMITED_API extern LIBSHIBOKEN_API PyTypeObject *PepStaticMethod_TypePtr; +LIBSHIBOKEN_API PyObject *PyStaticMethod_New(PyObject *callable); #else #define PepStaticMethod_TypePtr &PyStaticMethod_Type #endif +// Although not PEP specific, we resolve this similar issue, here: +#if PY_VERSION_HEX < 0x03000000 +extern LIBSHIBOKEN_API PyTypeObject *PepMethodDescr_TypePtr; +#else +#define PepMethodDescr_TypePtr &PyMethodDescr_Type +#endif /***************************************************************************** * diff --git a/sources/shiboken2/libshiboken/pep384impl_doc.rst b/sources/shiboken2/libshiboken/pep384impl_doc.rst index 2844249ad..ab286dd3e 100644 --- a/sources/shiboken2/libshiboken/pep384impl_doc.rst +++ b/sources/shiboken2/libshiboken/pep384impl_doc.rst @@ -283,7 +283,9 @@ written that skips over dotted name parts. Finally, the function ``_PyObject_Dump`` was excluded from the limited API. This is a useful debugging aid that we always want to have available, -so it is added back, again. +so it is added back, again. Anyway, we did not reimplement it, and so +Windows is not supported. +Therefore, a forgotten debugging call of this functions will break COIN. :-) Using The New Type API diff --git a/sources/shiboken2/libshiboken/qt_attribution.json b/sources/shiboken2/libshiboken/qt_attribution.json index 12a0952df..071870780 100644 --- a/sources/shiboken2/libshiboken/qt_attribution.json +++ b/sources/shiboken2/libshiboken/qt_attribution.json @@ -7,6 +7,6 @@ "Homepage": "http://www.python.org/", "Version": "3.7.0", "License": "PSF LICENSE AGREEMENT FOR PYTHON 3.7.0", - "LicenseFile": "bufferprocs27.h", + "LicenseFile": "bufferprocs_py37.h", "Copyright": "© Copyright 2001-2018, Python Software Foundation." } diff --git a/sources/shiboken2/libshiboken/sbkconverter.cpp b/sources/shiboken2/libshiboken/sbkconverter.cpp index a13222de6..4f6ebf65f 100644 --- a/sources/shiboken2/libshiboken/sbkconverter.cpp +++ b/sources/shiboken2/libshiboken/sbkconverter.cpp @@ -78,7 +78,8 @@ void init() Primitive<unsigned int>::createConverter(), Primitive<unsigned long>::createConverter(), Primitive<unsigned short>::createConverter(), - VoidPtr::createConverter() + VoidPtr::createConverter(), + Primitive<std::nullptr_t>::createConverter() }; PrimitiveTypeConverters = primitiveTypeConverters; @@ -100,6 +101,7 @@ void init() converters["unsigned long"] = primitiveTypeConverters[SBK_UNSIGNEDLONG_IDX]; converters["unsigned short"] = primitiveTypeConverters[SBK_UNSIGNEDSHORT_IDX]; converters["void*"] = primitiveTypeConverters[SBK_VOIDPTR_IDX]; + converters["std::nullptr_t"] = primitiveTypeConverters[SBK_NULLPTR_T_IDX]; initArrayConverters(); } @@ -246,10 +248,8 @@ PythonToCppFunc isPythonToCppPointerConvertible(SbkObjectType *type, PyObject *p static inline PythonToCppFunc IsPythonToCppConvertible(const SbkConverter *converter, PyObject *pyIn) { assert(pyIn); - const ToCppConversionList& convs = converter->toCppConversions; - for (ToCppConversionList::const_iterator conv = convs.begin(), end = convs.end(); conv != end; ++conv) { - PythonToCppFunc toCppFunc = 0; - if ((toCppFunc = (*conv).first(pyIn))) + for (const ToCppConversion &c : converter->toCppConversions) { + if (PythonToCppFunc toCppFunc = c.first(pyIn)) return toCppFunc; } return 0; @@ -361,7 +361,7 @@ bool isImplicitConversion(SbkObjectType *type, PythonToCppFunc toCppFunc) // Note that we don't check if the Python to C++ conversion is in // the list of the type's conversions, for it is expected that the // caller knows what he's doing. - ToCppConversionList::iterator conv = PepType_SOTP(type)->converter->toCppConversions.begin(); + const auto conv = PepType_SOTP(type)->converter->toCppConversions.cbegin(); return toCppFunc != (*conv).second; } diff --git a/sources/shiboken2/libshiboken/sbkconverter.h b/sources/shiboken2/libshiboken/sbkconverter.h index 0effebf57..33c33025b 100644 --- a/sources/shiboken2/libshiboken/sbkconverter.h +++ b/sources/shiboken2/libshiboken/sbkconverter.h @@ -341,6 +341,7 @@ LIBSHIBOKEN_API bool pythonTypeIsWrapperType(const SbkConverter *converter); #define SBK_UNSIGNEDLONG_IDX 14 #define SBK_UNSIGNEDSHORT_IDX 15 #define SBK_VOIDPTR_IDX 16 +#define SBK_NULLPTR_T_IDX 17 template<typename T> SbkConverter* PrimitiveTypeConverter() { return 0; } template<> inline SbkConverter* PrimitiveTypeConverter<PY_LONG_LONG>() { return primitiveTypeConverter(SBK_PY_LONG_LONG_IDX); } @@ -360,6 +361,7 @@ template<> inline SbkConverter* PrimitiveTypeConverter<unsigned int>() { return template<> inline SbkConverter* PrimitiveTypeConverter<unsigned long>() { return primitiveTypeConverter(SBK_UNSIGNEDLONG_IDX); } template<> inline SbkConverter* PrimitiveTypeConverter<unsigned short>() { return primitiveTypeConverter(SBK_UNSIGNEDSHORT_IDX); } template<> inline SbkConverter* PrimitiveTypeConverter<void*>() { return primitiveTypeConverter(SBK_VOIDPTR_IDX); } +template<> inline SbkConverter* PrimitiveTypeConverter<std::nullptr_t>() { return primitiveTypeConverter(SBK_NULLPTR_T_IDX); } } // namespace Shiboken::Conversions @@ -386,6 +388,7 @@ template<> inline PyTypeObject* SbkType<unsigned char>() { return &PyInt_Type; } template<> inline PyTypeObject* SbkType<unsigned int>() { return &PyLong_Type; } template<> inline PyTypeObject* SbkType<unsigned long>() { return &PyLong_Type; } template<> inline PyTypeObject* SbkType<unsigned short>() { return &PyInt_Type; } +template<> inline PyTypeObject* SbkType<std::nullptr_t>() { return Py_TYPE(&_Py_NoneStruct); } } // namespace Shiboken diff --git a/sources/shiboken2/libshiboken/sbkconverter_p.h b/sources/shiboken2/libshiboken/sbkconverter_p.h index f39608663..cb968ed89 100644 --- a/sources/shiboken2/libshiboken/sbkconverter_p.h +++ b/sources/shiboken2/libshiboken/sbkconverter_p.h @@ -43,11 +43,11 @@ #include "sbkpython.h" #include "sbkconverter.h" #include "sbkstring.h" -#include <list> #include <limits> #include <typeinfo> #include <sstream> #include <iostream> +#include <vector> #include "sbkdbg.h" @@ -55,7 +55,7 @@ extern "C" { typedef std::pair<IsConvertibleToCppFunc, PythonToCppFunc> ToCppConversion; -typedef std::list<ToCppConversion> ToCppConversionList; +typedef std::vector<ToCppConversion> ToCppConversionVector; /** * \internal @@ -104,7 +104,7 @@ struct SbkConverter * For Object Types, that never have implicit conversions, this * list is always empty. */ - ToCppConversionList toCppConversions; + ToCppConversionVector toCppConversions; }; } // extern "C" @@ -533,6 +533,36 @@ struct Primitive<std::string> : TwoPrimitive<std::string> } }; +// nullptr_t +template <> +struct Primitive<std::nullptr_t> : TwoPrimitive<std::nullptr_t> +{ + static PyObject* toPython(const void* cppIn) + { + return Py_None; + } + static void toCpp(PyObject *, void *cppOut) + { + *reinterpret_cast<std::nullptr_t*>(cppOut) = nullptr; + } + static PythonToCppFunc isConvertible(PyObject* pyIn) + { + if (pyIn == Py_None) + return toCpp; + return nullptr; + } + static void otherToCpp(PyObject* pyIn, void* cppOut) + { + *reinterpret_cast<std::nullptr_t*>(cppOut) = nullptr; + } + static PythonToCppFunc isOtherConvertible(PyObject* pyIn) + { + if (pyIn == nullptr) + return otherToCpp; + return nullptr; + } +}; + namespace Shiboken { namespace Conversions { SbkConverter *createConverterObject(PyTypeObject *type, diff --git a/sources/shiboken2/libshiboken/sbkdbg.h b/sources/shiboken2/libshiboken/sbkdbg.h index c26816bbd..fdaf2a27a 100644 --- a/sources/shiboken2/libshiboken/sbkdbg.h +++ b/sources/shiboken2/libshiboken/sbkdbg.h @@ -63,6 +63,11 @@ class BaseLogger { public: + BaseLogger(const BaseLogger&) = delete; + BaseLogger(BaseLogger&&) = delete; + BaseLogger& operator=(const BaseLogger&) = delete; + BaseLogger& operator=(BaseLogger&&) = delete; + BaseLogger(std::ostream& output, const char* function, const char* context) : m_stream(output), m_function(function), m_context(context) {} ~BaseLogger() diff --git a/sources/shiboken2/libshiboken/sbkenum.cpp b/sources/shiboken2/libshiboken/sbkenum.cpp index d129e6380..26b40c3cb 100644 --- a/sources/shiboken2/libshiboken/sbkenum.cpp +++ b/sources/shiboken2/libshiboken/sbkenum.cpp @@ -47,9 +47,11 @@ #include <string.h> #include <cstring> -#include <list> +#include <vector> #define SBK_ENUM(ENUM) reinterpret_cast<SbkEnumObject*>(ENUM) +#define SBK_TYPE_CHECK(o) (strcmp(Py_TYPE(Py_TYPE(o))->tp_name, "Shiboken.EnumType") == 0) +typedef PyObject* (*enum_func)(PyObject*, PyObject*); extern "C" { @@ -75,7 +77,7 @@ struct SbkEnumObject static PyObject* SbkEnumObject_repr(PyObject* self) { - const SbkEnumObject *enumObj = reinterpret_cast<SbkEnumObject *>(self); + const SbkEnumObject *enumObj = SBK_ENUM(self); if (enumObj->ob_name) return Shiboken::String::fromFormat("%s.%s", (Py_TYPE(self))->tp_name, PyBytes_AS_STRING(enumObj->ob_name)); else @@ -84,7 +86,7 @@ static PyObject* SbkEnumObject_repr(PyObject* self) static PyObject* SbkEnumObject_name(PyObject* self, void*) { - SbkEnumObject *enum_self = reinterpret_cast<SbkEnumObject *>(self); + SbkEnumObject *enum_self = SBK_ENUM(self); if (enum_self->ob_name == NULL) Py_RETURN_NONE; @@ -113,6 +115,43 @@ static PyObject* SbkEnum_tp_new(PyTypeObject *type, PyObject *args, PyObject *) return reinterpret_cast<PyObject*>(self); } +static PyObject* enum_op(enum_func f, PyObject *a, PyObject *b) { + PyObject *valA = a; + PyObject *valB = b; + PyObject *result = nullptr; + bool enumA = false; + bool enumB = false; + + // We are not allowing floats + if (!PyFloat_Check(valA) && !PyFloat_Check(valB)) { + // Check if both variables are SbkEnumObject + if (SBK_TYPE_CHECK(valA)) { + valA = PyLong_FromLong(SBK_ENUM(valA)->ob_value); + enumA = true; + } + if (SBK_TYPE_CHECK(valB)) { + valB = PyLong_FromLong(SBK_ENUM(valB)->ob_value); + enumB = true; + } + } + + // Without an enum we are not supporting the operation + if (!(enumA || enumB)) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } else { + result = f(valA, valB); + } + + // Decreasing the reference of the used variables a and b. + if (enumA) + Py_DECREF(valA); + if (enumB) + Py_DECREF(valB); + + return result; +} + /* Notes: * On Py3k land we use long type when using integer numbers. However, on older * versions of Python (version 2) we need to convert it to int type, @@ -126,48 +165,19 @@ static PyObject* enum_int(PyObject* v) return PyInt_FromLong(SBK_ENUM(v)->ob_value); } -static long getNumberValue(PyObject* v) -{ - PyObject* number = PyNumber_Long(v); - long result = PyLong_AsLong(number); - Py_XDECREF(number); - return result; -} - static PyObject* enum_and(PyObject* self, PyObject* b) { - if (!PyNumber_Check(b)) { - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } - - long valA = SBK_ENUM(self)->ob_value; - long valB = getNumberValue(b); - return PyInt_FromLong(valA & valB); + return enum_op(PyNumber_And, self, b); } static PyObject* enum_or(PyObject* self, PyObject* b) { - if (!PyNumber_Check(b)) { - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } - - long valA = SBK_ENUM(self)->ob_value; - long valB = getNumberValue(b); - return PyInt_FromLong(valA | valB); +return enum_op(PyNumber_Or, self, b); } static PyObject* enum_xor(PyObject* self, PyObject* b) { - if (!PyNumber_Check(b)) { - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } - - long valA = SBK_ENUM(self)->ob_value; - long valB = getNumberValue(b); - return PyInt_FromLong(valA ^ valB); + return enum_op(PyNumber_Xor, self, b); } static int enum_bool(PyObject* v) @@ -177,72 +187,63 @@ static int enum_bool(PyObject* v) static PyObject* enum_add(PyObject* self, PyObject* v) { - long valA = SBK_ENUM(self)->ob_value; - long valB = getNumberValue(v); - return PyInt_FromLong(valA + valB); + return enum_op(PyNumber_Add, self, v); } static PyObject* enum_subtract(PyObject* self, PyObject* v) { - long valA = SBK_ENUM(self)->ob_value; - long valB = getNumberValue(v); - return PyInt_FromLong(valA - valB); + return enum_op(PyNumber_Subtract, self, v); } static PyObject* enum_multiply(PyObject* self, PyObject* v) { - long valA = SBK_ENUM(self)->ob_value; - long valB = getNumberValue(v); - return PyInt_FromLong(valA * valB); +return enum_op(PyNumber_Multiply, self, v); } #ifndef IS_PY3K static PyObject* enum_divide(PyObject* self, PyObject* v) { - long valA = SBK_ENUM(self)->ob_value; - long valB = getNumberValue(v); - return PyLong_FromLong(valA / valB); + return enum_op(PyNumber_Divide, self, v); } #endif static PyObject* enum_richcompare(PyObject* self, PyObject* other, int op) { - int result = 0; - if (!PyNumber_Check(other)) { + PyObject *valA = self; + PyObject *valB = other; + PyObject *result = nullptr; + bool enumA = false; + bool enumB = false; + + // We are not allowing floats + if (!PyFloat_Check(valA) && !PyFloat_Check(valB)) { + + // Check if both variables are SbkEnumObject + if (SBK_TYPE_CHECK(valA)) { + valA = PyLong_FromLong(SBK_ENUM(valA)->ob_value); + enumA = true; + } + if (SBK_TYPE_CHECK(valB)) { + valB = PyLong_FromLong(SBK_ENUM(valB)->ob_value); + enumB =true; + } + } + + // Without an enum we are not supporting the operation + if (!(enumA || enumB)) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; + } else { + result = PyObject_RichCompare(valA, valB, op); } - long valA = SBK_ENUM(self)->ob_value; - long valB = getNumberValue(other); - - switch (op) { - case Py_EQ: - result = (valA == valB); - break; - case Py_NE: - result = (valA != valB); - break; - case Py_LE: - result = (valA <= valB); - break; - case Py_GE: - result = (valA >= valB); - break; - case Py_LT: - result = (valA < valB); - break; - case Py_GT: - result = (valA > valB); - break; - default: - PyErr_BadArgument(); - return NULL; - } - if (result) - Py_RETURN_TRUE; - else - Py_RETURN_FALSE; + // Decreasing the reference of the used variables a and b. + if (enumA) + Py_DECREF(valA); + if (enumB) + Py_DECREF(valB); + + return result; } static Py_hash_t enum_hash(PyObject* pyObj) @@ -339,15 +340,18 @@ namespace Shiboken { class DeclaredEnumTypes { public: + DeclaredEnumTypes(const DeclaredEnumTypes&) = delete; + DeclaredEnumTypes(DeclaredEnumTypes&&) = delete; + DeclaredEnumTypes& operator=(const DeclaredEnumTypes&) = delete; + DeclaredEnumTypes& operator=(DeclaredEnumTypes&&) = delete; + DeclaredEnumTypes(); ~DeclaredEnumTypes(); static DeclaredEnumTypes& instance(); void addEnumType(PyTypeObject* type); private: - DeclaredEnumTypes(const DeclaredEnumTypes&); - DeclaredEnumTypes& operator=(const DeclaredEnumTypes&); - std::list<PyTypeObject*> m_enumTypes; + std::vector<PyTypeObject *> m_enumTypes; }; namespace Enum { @@ -373,7 +377,9 @@ PyObject* getEnumItemFromValue(PyTypeObject* enumType, long itemValue) return 0; } -static PyTypeObject* createEnum(const char* fullName, const char* cppName, const char* shortName, PyTypeObject* flagsType) +static PyTypeObject* createEnum(const char* fullName, const char* cppName, + const char* /* shortName */, + PyTypeObject* flagsType) { PyTypeObject* enumType = newTypeWithName(fullName, cppName, flagsType); if (PyType_Ready(enumType) < 0) @@ -524,13 +530,8 @@ copyNumberMethods(PyTypeObject *flagsType, int *pidx) { int idx = *pidx; -#ifdef IS_PY3K -# define SLOT slot -#else -# define SLOT slot_ -#endif #define PUT_SLOT(name) \ - number_slots[idx].SLOT = (name); \ + number_slots[idx].slot = (name); \ number_slots[idx].pfunc = PyType_GetSlot(flagsType, (name)); \ ++idx; @@ -593,8 +594,8 @@ newTypeWithName(const char* name, newspec->flags = SbkNewType_spec.flags; // we must append all the number methods, so rebuild everything: int idx = 0; - while (SbkNewType_slots[idx].SLOT) { - newslots[idx].SLOT = SbkNewType_slots[idx].SLOT; + while (SbkNewType_slots[idx].slot) { + newslots[idx].slot = SbkNewType_slots[idx].slot; newslots[idx].pfunc = SbkNewType_slots[idx].pfunc; ++idx; } @@ -644,14 +645,10 @@ DeclaredEnumTypes& DeclaredEnumTypes::instance() return me; } -DeclaredEnumTypes::DeclaredEnumTypes() -{ -} +DeclaredEnumTypes::DeclaredEnumTypes() = default; DeclaredEnumTypes::~DeclaredEnumTypes() { - std::list<PyTypeObject*>::const_iterator it = m_enumTypes.begin(); - for (; it != m_enumTypes.end(); ++it) { /* * PYSIDE-595: This was "delete *it;" before introducing 'PyType_FromSpec'. * XXX what should I do now? @@ -660,8 +657,8 @@ DeclaredEnumTypes::~DeclaredEnumTypes() * So right now I am doing nothing. Surely wrong but no crash. * See also the comment in function 'createGlobalEnumItem'. */ - //fprintf(stderr, "ttt %d %s\n", Py_REFCNT(*it), *it->tp_name); - } + // for (PyTypeObject *o : m_enumTypes) + // fprintf(stderr, "ttt %d %s\n", Py_REFCNT(o), o->tp_name); m_enumTypes.clear(); } diff --git a/sources/shiboken2/libshiboken/sbkpython.h b/sources/shiboken2/libshiboken/sbkpython.h index 29e25605a..f06b0b19e 100644 --- a/sources/shiboken2/libshiboken/sbkpython.h +++ b/sources/shiboken2/libshiboken/sbkpython.h @@ -42,19 +42,65 @@ #include "sbkversion.h" +// Qt's "slots" macro collides with the "slots" member variables +// used in some Python structs. For compilers that support push_macro, +// temporarily undefine it. +#if defined(slots) && (defined(__GNUC__) || defined(_MSC_VER) || defined(__clang__)) +# pragma push_macro("slots") +# undef slots /* * Python 2 has function _Py_Mangle directly in Python.h . * This creates wrong language binding unless we define 'extern "C"' here. */ extern "C" { -#include <Python.h> +/* + * Python 2 uses the "register" keyword, which is deprecated in C++ 11 + * and forbidden in C++17. + */ +# if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdeprecated-register" +# endif + +# include <Python.h> + +# if defined(__clang__) +# pragma clang diagnostic pop +# endif } -#include <structmember.h> +# include <structmember.h> // Now we have the usual variables from Python.h . -#include "python25compat.h" -#include "shibokenmacros.h" -#include "pep384impl.h" -#include "typespec.h" +# include "python25compat.h" +# include "shibokenmacros.h" +# include "pep384impl.h" +# include "typespec.h" +# pragma pop_macro("slots") + +#else + +extern "C" { +/* + * Python 2 uses the "register" keyword, which is deprecated in C++ 11 + * and forbidden in C++17. + */ +# if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdeprecated-register" +# endif + +# include <Python.h> + +# if defined(__clang__) +# pragma clang diagnostic pop +# endif +} +# include <structmember.h> +// Now we have the usual variables from Python.h . +# include "python25compat.h" +# include "shibokenmacros.h" +# include "pep384impl.h" +# include "typespec.h" +#endif #if PY_MAJOR_VERSION >= 3 #define IS_PY3K diff --git a/sources/shiboken2/libshiboken/sbkstring.cpp b/sources/shiboken2/libshiboken/sbkstring.cpp index 6ca35f12e..a3ffcdabb 100644 --- a/sources/shiboken2/libshiboken/sbkstring.cpp +++ b/sources/shiboken2/libshiboken/sbkstring.cpp @@ -66,10 +66,7 @@ bool check(PyObject* obj) bool checkChar(PyObject* pyobj) { - if (check(pyobj) && (len(pyobj) == 1)) - return true; - - return false; + return check(pyobj) && (len(pyobj) == 1); } bool isConvertible(PyObject* obj) diff --git a/sources/shiboken2/libshiboken/shibokenbuffer.cpp b/sources/shiboken2/libshiboken/shibokenbuffer.cpp index dc29b40a7..a691a31ee 100644 --- a/sources/shiboken2/libshiboken/shibokenbuffer.cpp +++ b/sources/shiboken2/libshiboken/shibokenbuffer.cpp @@ -58,6 +58,7 @@ void* Shiboken::Buffer::getPointer(PyObject* pyObj, Py_ssize_t* size) PyBuffer_Release(&view); return view.buf; } + return nullptr; #else Py_ssize_t bufferSize = 0; @@ -85,7 +86,8 @@ PyObject* Shiboken::Buffer::newObject(void* memory, Py_ssize_t size, Type type) view.shape = shape; // Pep384: This is way too complicated and impossible with the limited api: //return PyMemoryView_FromBuffer(&view); - return PyMemoryView_FromMemory((char *)view.buf, size, type == ReadOnly ? PyBUF_READ : PyBUF_WRITE); + return PyMemoryView_FromMemory(reinterpret_cast<char *>(view.buf), + size, type == ReadOnly ? PyBUF_READ : PyBUF_WRITE); #else return type == ReadOnly ? PyBuffer_FromMemory(memory, size) : PyBuffer_FromReadWriteMemory(memory, size); #endif diff --git a/sources/shiboken2/libshiboken/signature.cpp b/sources/shiboken2/libshiboken/signature.cpp index f0bb8e609..922f85906 100644 --- a/sources/shiboken2/libshiboken/signature.cpp +++ b/sources/shiboken2/libshiboken/signature.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt for Python. @@ -38,6 +38,7 @@ ****************************************************************************/ #include "basewrapper.h" +#include "autodecref.h" extern "C" { @@ -77,18 +78,25 @@ typedef struct safe_globals_struc { static safe_globals pyside_globals = 0; -static PyObject *GetSignature_Function(PyCFunctionObject *); -static PyObject *GetSignature_TypeMod(PyObject *); +static PyObject *GetClassKey(PyObject *ob); + +static PyObject *GetSignature_Function(PyObject *, const char *); +static PyObject *GetSignature_TypeMod(PyObject *, const char *); +static PyObject *GetSignature_Wrapper(PyObject *, const char *); +static PyObject *get_signature(PyObject *self, PyObject *args); static PyObject *PySide_BuildSignatureProps(PyObject *class_mod); +static void init_module_1(void); +static void init_module_2(void); + const char helper_module_name[] = "signature_loader"; const char bootstrap_name[] = "bootstrap"; const char arg_name[] = "pyside_arg_dict"; const char func_name[] = "pyside_type_init"; static PyObject * -CreateSignature(PyObject *props, const char *sig_kind) +CreateSignature(PyObject *props, PyObject *key) { /* * Here is the new function to create all signatures. It simply calls @@ -97,30 +105,95 @@ CreateSignature(PyObject *props, const char *sig_kind) * to support '_signature_is_functionlike()'. */ return PyObject_CallFunction(pyside_globals->createsig_func, - (char *)"(Os)", props, sig_kind); + (char *)"(OO)", props, key); } static PyObject * -pyside_cf_get___signature__(PyObject *func) +pyside_cf_get___signature__(PyObject *func, const char *modifier) { - return GetSignature_Function((PyCFunctionObject *)func); + init_module_2(); + return GetSignature_Function(func, modifier); } static PyObject * -pyside_sm_get___signature__(PyObject *sm) +pyside_sm_get___signature__(PyObject *sm, const char *modifier) { - PyObject *func, *ret; + init_module_2(); + Shiboken::AutoDecRef func(PyObject_GetAttrString(sm, "__func__")); + if (Py_TYPE(func) == PepFunction_TypePtr) + Py_RETURN_NONE; + return GetSignature_Function(func, modifier); +} - func = PyObject_GetAttrString(sm, "__func__"); - ret = GetSignature_Function((PyCFunctionObject *)func); - Py_XDECREF(func); - return ret; +static PyObject * +_get_class_of_cf(PyObject *ob_cf) +{ + PyObject *selftype = PyCFunction_GET_SELF(ob_cf); + if (selftype == NULL) + selftype = PyDict_GetItem(pyside_globals->map_dict, (PyObject *)ob_cf); + if (selftype == NULL) { + if (!PyErr_Occurred()) + Py_RETURN_NONE; + return NULL; + } + PyObject *typemod = (PyType_Check(selftype) || PyModule_Check(selftype)) + ? selftype : (PyObject *)Py_TYPE(selftype); + // do we support module functions? + Py_INCREF(typemod); + return typemod; +} + +static PyObject * +_get_class_of_sm(PyObject *ob_sm) +{ + Shiboken::AutoDecRef func(PyObject_GetAttrString(ob_sm, "__func__")); + return _get_class_of_cf(func); } -#ifdef Py_LIMITED_API +static PyObject * +_get_class_of_descr(PyObject *ob) +{ + Shiboken::AutoDecRef func_name(PyObject_GetAttrString(ob, "__name__")); + return PyObject_GetAttrString(ob, "__objclass__"); +} + +static PyObject * +GetClassOfFunc(PyObject *ob) +{ + if (PyType_Check(ob)) + return ob; + if (Py_TYPE(ob) == &PyCFunction_Type) + return _get_class_of_cf(ob); + if (Py_TYPE(ob) == PepStaticMethod_TypePtr) + return _get_class_of_sm(ob); + if (Py_TYPE(ob) == PepMethodDescr_TypePtr) + return _get_class_of_descr(ob); + if (Py_TYPE(ob) == &PyWrapperDescr_Type) + return _get_class_of_descr(ob); + Py_FatalError("unexpected type in GetClassOfFunc"); + return nullptr; +} + +static PyObject * +compute_name_key(PyObject *ob) +{ + if (PyType_Check(ob)) + return GetClassKey(GetClassOfFunc(ob)); + PyObject *func = ob; + if (Py_TYPE(ob) == PepStaticMethod_TypePtr) + func = PyObject_GetAttrString(ob, "__func__"); + else + Py_INCREF(func); + Shiboken::AutoDecRef func_name(PyObject_GetAttrString(func, "__name__")); + Py_DECREF(func); + if (func_name.isNull()) + Py_FatalError("unexpected name problem in compute_name_key"); + Shiboken::AutoDecRef type_key(GetClassKey(GetClassOfFunc(ob))); + return Py_BuildValue("(OO)", type_key.object(), func_name.object()); +} static int -build_qualname_to_func(PyObject *obtype) +build_name_key_to_func(PyObject *obtype) { PyTypeObject *type = (PyTypeObject *)obtype; PyMethodDef *meth = type->tp_methods; @@ -129,230 +202,245 @@ build_qualname_to_func(PyObject *obtype) return 0; for (; meth->ml_name != NULL; meth++) { - PyObject *func = PyCFunction_NewEx(meth, obtype, NULL); - PyObject *qualname = PyObject_GetAttrString(func, "__qualname__"); - if (func == NULL || qualname == NULL) { - return -1; - } - if (PyDict_SetItem(pyside_globals->map_dict, qualname, func) < 0) { + Shiboken::AutoDecRef func(PyCFunction_NewEx(meth, obtype, NULL)); + Shiboken::AutoDecRef name_key(compute_name_key(func)); + if (func.isNull() || name_key.isNull() + || PyDict_SetItem(pyside_globals->map_dict, name_key, func) < 0) return -1; - } - Py_DECREF(func); - Py_DECREF(qualname); } return 0; } static PyObject * -qualname_to_typename(PyObject *qualname) -{ - PyObject *func = PyObject_GetAttrString(qualname, "split"); - PyObject *list = func ? PyObject_CallFunction(func, (char *)"(s)", ".") - : NULL; - PyObject *res = list ? PyList_GetItem(list, 0) : NULL; - Py_XINCREF(res); - Py_XDECREF(func); - Py_XDECREF(list); - return res; -} - -static PyObject * -qualname_to_func(PyObject *ob) +name_key_to_func(PyObject *ob) { /* - * If we have __qualname__, then we can easily build a mapping - * from __qualname__ to PyCFunction. This is necessary when - * the limited API does not let us go easily from descriptor - * to PyMethodDef. + * We build a mapping from name_key to function. + * This could also be computed directly, but the Limited API + * makes this impossible. So we always build our own mapping. */ - PyObject *ret; - PyObject *qualname = PyObject_GetAttrString((PyObject *)ob, - "__qualname__"); - if (qualname != NULL) { - ret = PyDict_GetItem(pyside_globals->map_dict, qualname); - if (ret == NULL) { - // do a lazy initialization - PyObject *type_name = qualname_to_typename(qualname); - PyObject *type = PyDict_GetItem(pyside_globals->map_dict, - type_name); - Py_XDECREF(type_name); - if (type == NULL) - Py_RETURN_NONE; - if (build_qualname_to_func(type) < 0) - return NULL; - ret = PyDict_GetItem(pyside_globals->map_dict, qualname); - } - Py_XINCREF(ret); - Py_DECREF(qualname); - } - else + Shiboken::AutoDecRef name_key(compute_name_key(ob)); + if (name_key.isNull()) Py_RETURN_NONE; + + PyObject *ret = PyDict_GetItem(pyside_globals->map_dict, name_key); + if (ret == NULL) { + // do a lazy initialization + Shiboken::AutoDecRef type_key(GetClassKey(GetClassOfFunc(ob))); + PyObject *type = PyDict_GetItem(pyside_globals->map_dict, + type_key); + if (type == nullptr) + Py_RETURN_NONE; + assert(PyType_Check(type)); + if (build_name_key_to_func(type) < 0) + return NULL; + ret = PyDict_GetItem(pyside_globals->map_dict, name_key); + } + Py_XINCREF(ret); return ret; } -#endif static PyObject * -pyside_md_get___signature__(PyObject *ob) -{ - PyObject *func; - PyObject *result; -#ifndef Py_LIMITED_API - PyMethodDescrObject *descr = (PyMethodDescrObject *)ob; - -# if PYTHON_USES_D_COMMON - func = PyCFunction_NewEx(descr->d_method, - (PyObject *)descr->d_common.d_type, NULL); -# else - func = PyCFunction_NewEx(descr->d_method, - (PyObject *)descr->d_type, NULL); -# endif -#else - /* - * With limited access, we cannot use the fields of a method descriptor, - * but in Python 3 we have the __qualname__ field which allows us to - * grab the method object from our registry. - */ - func = qualname_to_func(ob); -#endif - if (func == Py_None) +pyside_md_get___signature__(PyObject *ob_md, const char *modifier) +{ + init_module_2(); + Shiboken::AutoDecRef func(name_key_to_func(ob_md)); + if (func.object() == Py_None) return Py_None; - if (func == NULL) + if (func.isNull()) Py_FatalError("missing mapping in MethodDescriptor"); - result = pyside_cf_get___signature__(func); - Py_DECREF(func); - return result; + return pyside_cf_get___signature__(func, modifier); } static PyObject * -pyside_tp_get___signature__(PyObject *typemod) +pyside_wd_get___signature__(PyObject *ob, const char *modifier) { - return GetSignature_TypeMod(typemod); + init_module_2(); + return GetSignature_Wrapper(ob, modifier); } static PyObject * -GetSignature_Function(PyCFunctionObject *func) +pyside_tp_get___signature__(PyObject *typemod, const char *modifier) { - PyObject *typemod, *type_name, *dict, *props, *value, *selftype; - PyObject *func_name = PyObject_GetAttrString((PyObject *)func, "__name__"); - const char *sig_kind; - int flags; + init_module_2(); + return GetSignature_TypeMod(typemod, modifier); +} - selftype = PyCFunction_GET_SELF((PyObject *)func); - if (selftype == NULL) - selftype = PyDict_GetItem(pyside_globals->map_dict, (PyObject *)func); - if (selftype == NULL) { - if (!PyErr_Occurred()) { - PyErr_Format(PyExc_SystemError, - "the signature for \"%s\" should exist", - PepCFunction_GET_NAMESTR(func) - ); - } - return NULL; +// forward +static PyObject * +GetSignature_Cached(PyObject *props, const char *sig_kind, const char *modifier); + +static PyObject * +GetClassKey(PyObject *ob) +{ + assert(PyType_Check(ob) || PyModule_Check(ob)); + /* + * We obtain a unique key using the module name and the class name. + * + * The class name is a bit funny when modules are nested. + * Example: + * + * "sample.Photon.ValueIdentity" is a class. + * name: "ValueIdentity" + * module: "sample.Photon" + * + * This is the PyCFunction behavior, as opposed to Python functions. + */ + Shiboken::AutoDecRef class_name(PyObject_GetAttrString(ob, "__name__")); + Shiboken::AutoDecRef module_name(PyObject_GetAttrString(ob, "__module__")); + + if (module_name.isNull()) + PyErr_Clear(); + + // Note: if we have a module, then __module__ is null, and we get + // the module name through __name__ . + if (class_name.isNull()) + return nullptr; + if (module_name.object()) + return Py_BuildValue("(OO)", module_name.object(), class_name.object()); + return Py_BuildValue("O", class_name.object()); +} + +static PyObject *empty_dict = nullptr; + +static PyObject * +TypeKey_to_PropsDict(PyObject *type_key, PyObject *obtype) +{ + PyObject *dict = PyDict_GetItem(pyside_globals->arg_dict, type_key); + if (dict == nullptr) { + if (empty_dict == nullptr) + empty_dict = PyDict_New(); + dict = empty_dict; } - if ((PyType_Check(selftype) || PyModule_Check(selftype))) - typemod = selftype; - else - typemod = (PyObject *)Py_TYPE(selftype); - type_name = PyObject_GetAttrString(typemod, "__name__"); - if (type_name == NULL) + if (PyTuple_Check(dict)) + dict = PySide_BuildSignatureProps(obtype); + return dict; +} + +static PyObject * +GetSignature_Function(PyObject *ob_func, const char *modifier) +{ + // make sure that we look into PyCFunction, only... + if (Py_TYPE(ob_func) == PepFunction_TypePtr) Py_RETURN_NONE; - dict = PyDict_GetItem(pyside_globals->arg_dict, type_name); - Py_DECREF(type_name); - if (dict == NULL) + Shiboken::AutoDecRef typemod(GetClassOfFunc(ob_func)); + Shiboken::AutoDecRef type_key(GetClassKey(typemod)); + if (type_key.isNull()) Py_RETURN_NONE; - if (PyTuple_Check(dict)) { - /* - * We do the initialization lazily. - * This has also the advantage that we can freely import PySide. - */ - dict = PySide_BuildSignatureProps(typemod); - if (dict == NULL) - Py_RETURN_NONE; - } - props = PyDict_GetItem(dict, func_name); - if (props == NULL) + PyObject *dict = TypeKey_to_PropsDict(type_key, typemod); + if (dict == nullptr) + return nullptr; + Shiboken::AutoDecRef func_name(PyObject_GetAttrString(ob_func, "__name__")); + PyObject *props = !func_name.isNull() ? PyDict_GetItem(dict, func_name) : nullptr; + if (props == nullptr) Py_RETURN_NONE; - flags = PyCFunction_GET_FLAGS((PyObject *)func); - if (flags & METH_CLASS) + + int flags = PyCFunction_GET_FLAGS(ob_func); + const char *sig_kind; + if (PyModule_Check(typemod)) + sig_kind = "function"; + else if (flags & METH_CLASS) sig_kind = "classmethod"; else if (flags & METH_STATIC) sig_kind = "staticmethod"; else sig_kind = "method"; - value = PyDict_GetItemString(props, sig_kind); - if (value == NULL) { - // we need to compute a signature object - value = CreateSignature(props, sig_kind); - if (value != NULL) { - if (PyDict_SetItemString(props, sig_kind, value) < 0) - return NULL; - } - else - Py_RETURN_NONE; - } - return Py_INCREF(value), value; + return GetSignature_Cached(props, sig_kind, modifier); } static PyObject * -GetSignature_TypeMod(PyObject *ob) +GetSignature_Wrapper(PyObject *ob, const char *modifier) { - PyObject *ob_name, *dict, *props, *value; - const char *sig_kind; - - ob_name = PyObject_GetAttrString(ob, "__name__"); - dict = PyDict_GetItem(pyside_globals->arg_dict, ob_name); - if (dict == NULL) + Shiboken::AutoDecRef func_name(PyObject_GetAttrString(ob, "__name__")); + Shiboken::AutoDecRef objclass(PyObject_GetAttrString(ob, "__objclass__")); + Shiboken::AutoDecRef class_key(GetClassKey(objclass)); + + if (func_name.isNull() || objclass.isNull() || class_key.isNull()) + return nullptr; + PyObject *dict = TypeKey_to_PropsDict(class_key, objclass); + if (dict == nullptr) + return nullptr; + PyObject *props = PyDict_GetItem(dict, func_name); + if (props == nullptr) Py_RETURN_NONE; - if (PyTuple_Check(dict)) { - dict = PySide_BuildSignatureProps(ob); - if (dict == NULL) { - Py_RETURN_NONE; - } - } - props = PyDict_GetItem(dict, ob_name); - Py_DECREF(ob_name); - if (props == NULL) + return GetSignature_Cached(props, "method", modifier); +} + +static PyObject * +GetSignature_TypeMod(PyObject *ob, const char *modifier) +{ + Shiboken::AutoDecRef ob_name(PyObject_GetAttrString(ob, "__name__")); + Shiboken::AutoDecRef ob_key(GetClassKey(ob)); + + PyObject *dict = TypeKey_to_PropsDict(ob_key, ob); + if (dict == nullptr) + return nullptr; + PyObject *props = PyDict_GetItem(dict, ob_name); + if (props == nullptr) Py_RETURN_NONE; - sig_kind = "method"; - value = PyDict_GetItemString(props, sig_kind); - if (value == NULL) { + return GetSignature_Cached(props, "method", modifier); +} + +static PyObject * +GetSignature_Cached(PyObject *props, const char *sig_kind, const char *modifier) +{ + Shiboken::AutoDecRef key(modifier == nullptr + ? Py_BuildValue("s", sig_kind) + : Py_BuildValue("(ss)", sig_kind, modifier)); + PyObject *value = PyDict_GetItem(props, key); + if (value == nullptr) { // we need to compute a signature object - value = CreateSignature(props, sig_kind); - if (value != NULL) { - if (PyDict_SetItemString(props, sig_kind, value) < 0) - return NULL; + value = CreateSignature(props, key); + if (value != nullptr) { + if (PyDict_SetItem(props, key, value) < 0) + // this is an error + return nullptr; } - else + else { + // key not found Py_RETURN_NONE; + } } return Py_INCREF(value), value; } - static const char PySide_PythonCode[] = - "from __future__ import print_function, absolute_import\n" - "import sys, os, traceback\n" - - "pyside_package_dir = os.environ.get('PYSIDE_PACKAGE_DIR', '.')\n" - "__file__ = os.path.join(pyside_package_dir, 'support', 'signature', 'loader.py')\n" - - "def bootstrap():\n" - " try:\n" - " with open(__file__) as _f:\n" - " exec(compile(_f.read(), __file__, 'exec'))\n" - " except Exception as e:\n" - " print('Exception:', e)\n" - " traceback.print_exc(file=sys.stdout)\n" - " globals().update(locals())\n" - ; + "from __future__ import print_function, absolute_import\n" R"~(if True: + + # This is becoming the 'signature_loader' module. + + import sys, os, traceback + # We avoid imports in phase 1 that could fail. "import shiboken" of the + # binary would even crash in FinishSignatureInitialization. + + def bootstrap(): + global __file__ + try: + import shiboken2 as root + except ImportError: + # uninstalled case without ctest, try only this one which has __init__: + from shibokenmodule import shiboken2 as root + rp = os.path.realpath(os.path.dirname(root.__file__)) + # This can be the shiboken2 directory or the binary module, so search. + while len(rp) > 3 and not os.path.exists(os.path.join(rp, 'support')): + rp = os.path.abspath(os.path.join(rp, '..')) + __file__ = os.path.join(rp, 'support', 'signature', 'loader.py') + try: + with open(__file__) as _f: + exec(compile(_f.read(), __file__, 'exec')) + except Exception as e: + print('Exception:', e) + traceback.print_exc(file=sys.stdout) + globals().update(locals()) + + )~"; static safe_globals_struc * init_phase_1(void) { - safe_globals_struc *p; PyObject *d, *v; - - p = (safe_globals_struc *)malloc(sizeof(safe_globals_struc)); + safe_globals_struc *p = (safe_globals_struc *) + malloc(sizeof(safe_globals_struc)); if (p == NULL) goto error; p->helper_module = PyImport_AddModule((char *) helper_module_name); @@ -373,11 +461,10 @@ init_phase_1(void) if (p->map_dict == NULL) goto error; - // Build a dict for the prepared arguments + // build a dict for the prepared arguments p->arg_dict = PyDict_New(); - if (p->arg_dict == NULL) - goto error; - if (PyObject_SetAttrString(p->helper_module, arg_name, p->arg_dict) < 0) + if (p->arg_dict == NULL + || PyObject_SetAttrString(p->helper_module, arg_name, p->arg_dict) < 0) goto error; return p; @@ -387,16 +474,24 @@ error: } static int -init_phase_2(safe_globals_struc *p) +init_phase_2(safe_globals_struc *p, PyMethodDef *methods) { - PyObject *bootstrap_func; - + PyObject *bootstrap_func, *v = nullptr; + PyMethodDef *ml; + + // The single function to be called, but maybe more to come. + for (ml = methods; ml->ml_name != NULL; ml++) { + v = PyCFunction_NewEx(ml, nullptr, nullptr); + if (v == nullptr + || PyObject_SetAttrString(p->helper_module, ml->ml_name, v) != 0) + goto error; + Py_DECREF(v); + } bootstrap_func = PyObject_GetAttrString(p->helper_module, bootstrap_name); - if (bootstrap_func == NULL) + if (bootstrap_func == NULL + || PyObject_CallFunction(bootstrap_func, (char *)"()") == NULL) goto error; - if (PyObject_CallFunction(bootstrap_func, (char *)"()") == NULL) - goto error; - // now the loader is initialized + // now the loader should be initialized p->sigparse_func = PyObject_GetAttrString(p->helper_module, func_name); if (p->sigparse_func == NULL) goto error; @@ -406,6 +501,8 @@ init_phase_2(safe_globals_struc *p) return 0; error: + Py_XDECREF(v); + PyErr_Print(); PyErr_SetString(PyExc_SystemError, "could not initialize part 2"); return -1; } @@ -413,20 +510,17 @@ error: static int add_more_getsets(PyTypeObject *type, PyGetSetDef *gsp) { + assert(PyType_Check(type)); + PyType_Ready(type); PyObject *dict = type->tp_dict; - for (; gsp->name != NULL; gsp++) { - PyObject *descr; if (PyDict_GetItemString(dict, gsp->name)) continue; - descr = PyDescr_NewGetSet(type, gsp); - if (descr == NULL) + Shiboken::AutoDecRef descr(PyDescr_NewGetSet(type, gsp)); + if (descr.isNull()) return -1; - if (PyDict_SetItemString(dict, gsp->name, descr) < 0) { - Py_DECREF(descr); + if (PyDict_SetItemString(dict, gsp->name, descr) < 0) return -1; - } - Py_DECREF(descr); } return 0; } @@ -463,6 +557,47 @@ static PyGetSetDef new_PyType_getsets[] = { {0} }; +static PyGetSetDef new_PyWrapperDescr_getsets[] = { + {(char *) "__signature__", (getter)pyside_wd_get___signature__}, + {0} +}; + +//////////////////////////////////////////////////////////////////////////// +// +// get_signature -- providing a superior interface +// +// Additionally to the interface via __signature__, we also provide +// a general function, which allows for different signature layouts. +// The "modifier" argument is a string that is passed in from loader.py . +// Configuration what the modifiers mean is completely in Python. +// + +static PyObject * +get_signature(PyObject *self, PyObject *args) +{ + PyObject *ob; + const char *modifier = nullptr; + + init_module_1(); + + if (!PyArg_ParseTuple(args, "O|s", &ob, &modifier)) + return NULL; + if (Py_TYPE(ob) == PepFunction_TypePtr) + Py_RETURN_NONE; + + if (Py_TYPE(ob) == &PyCFunction_Type) + return pyside_cf_get___signature__(ob, modifier); + if (Py_TYPE(ob) == PepStaticMethod_TypePtr) + return pyside_sm_get___signature__(ob, modifier); + if (Py_TYPE(ob) == PepMethodDescr_TypePtr) + return pyside_md_get___signature__(ob, modifier); + if (PyType_Check(ob)) + return pyside_tp_get___signature__(ob, modifier); + if (Py_TYPE(ob) == &PyWrapperDescr_Type) + return pyside_wd_get___signature__(ob, modifier); + Py_RETURN_NONE; +} + //////////////////////////////////////////////////////////////////////////// // // This special Type_Ready does certain initializations earlier with @@ -495,25 +630,22 @@ void handler(int sig) { #endif // _WIN32 static int -PySideType_Ready(PyTypeObject *type) +PySide_PatchTypes(void) { - PyObject *md; static int init_done = 0; if (!init_done) { - // Python2 does not expose certain types. We look them up: - // PyMethodDescr_Type 'type(str.__dict__["split"])' - // PyClassMethodDescr_Type. 'type(dict.__dict__["fromkeys"])' - // The latter is not needed until we use class methods in PySide. - md = PyObject_GetAttrString((PyObject *)&PyString_Type, "split"); - if (md == NULL + Shiboken::AutoDecRef md(PyObject_GetAttrString((PyObject *)&PyString_Type, "split")); // method-descriptor + Shiboken::AutoDecRef wd(PyObject_GetAttrString((PyObject *)Py_TYPE(Py_True), "__add__")); // wrapper-descriptor + if (md.isNull() || wd.isNull() || PyType_Ready(Py_TYPE(md)) < 0 - || add_more_getsets(Py_TYPE(md), new_PyMethodDescr_getsets) < 0 + || add_more_getsets(PepMethodDescr_TypePtr, new_PyMethodDescr_getsets) < 0 || add_more_getsets(&PyCFunction_Type, new_PyCFunction_getsets) < 0 || add_more_getsets(PepStaticMethod_TypePtr, new_PyStaticMethod_getsets) < 0 - || add_more_getsets(&PyType_Type, new_PyType_getsets) < 0) + || add_more_getsets(&PyType_Type, new_PyType_getsets) < 0 + || add_more_getsets(Py_TYPE(wd), new_PyWrapperDescr_getsets) < 0 + ) return -1; - Py_DECREF(md); #ifndef _WIN32 // We enable the stack trace in CI, only. const char *testEnv = getenv("QTEST_ENVIRONMENT"); @@ -522,58 +654,38 @@ PySideType_Ready(PyTypeObject *type) #endif // _WIN32 init_done = 1; } - return PyType_Ready(type); + return 0; } -static int -build_func_to_type(PyObject *obtype) +static void +init_module_1(void) { - PyTypeObject *type = (PyTypeObject *)obtype; - PyObject *dict = type->tp_dict; - PyMethodDef *meth = type->tp_methods; - - if (meth == 0) - return 0; + static int init_done = 0; - for (; meth->ml_name != NULL; meth++) { - if (meth->ml_flags & METH_STATIC) { - PyObject *descr = PyDict_GetItemString(dict, meth->ml_name); - if (descr == NULL) - return -1; - PyObject *func = PyObject_GetAttrString(descr, "__func__"); - if (func == NULL || - PyDict_SetItem(pyside_globals->map_dict, func, obtype) < 0) - return -1; - Py_DECREF(func); - } + if (!init_done) { + pyside_globals = init_phase_1(); + if (pyside_globals != nullptr) + init_done = 1; } - return 0; } static int PySide_BuildSignatureArgs(PyObject *module, PyObject *type, const char *signatures) { - PyObject *type_name, *arg_tup; - const char *name = NULL; - static int init_done = 0; + PyObject *type_key, *arg_tup; - if (!init_done) { - pyside_globals = init_phase_1(); - if (pyside_globals == NULL) - return -1; - init_done = 1; - } + init_module_1(); arg_tup = Py_BuildValue("(Os)", type, signatures); if (arg_tup == NULL) return -1; + /* + * We either get a module name or the dict of an EnclosingObject. + * We can ignore the EnclosingObject since we get full name info + * from the type. + */ if (!PyModule_Check(module)) - return 0; - name = PyModule_GetName(module); - if (name == NULL) - return -1; - if (strncmp(name, "PySide2.Qt", 10) != 0) - return 0; + assert(PyDict_Check(module)); /* * Normally, we would now just call the Python function with the * arguments and then continue processing. @@ -585,51 +697,69 @@ PySide_BuildSignatureArgs(PyObject *module, PyObject *type, * - by calling the python function late, we can freely import PySide * without recursion problems. */ - type_name = PyObject_GetAttrString(type, "__name__"); - if (type_name == NULL) + type_key = GetClassKey(type); + if (type_key == nullptr) return -1; - if (PyDict_SetItem(pyside_globals->arg_dict, type_name, arg_tup) < 0) + if (PyDict_SetItem(pyside_globals->arg_dict, type_key, arg_tup) < 0) return -1; /* - * We record also a mapping from type name to type. This helps to lazily - * initialize the Py_LIMITED_API in qualname_to_func(). + * We record also a mapping from type key to type. This helps to lazily + * initialize the Py_LIMITED_API in name_key_to_func(). */ - if (PyDict_SetItem(pyside_globals->map_dict, type_name, type) < 0) + + if (PyDict_SetItem(pyside_globals->map_dict, type_key, type) < 0) return -1; return 0; } -static PyObject * -PySide_BuildSignatureProps(PyObject *classmod) +static PyMethodDef signature_methods[] = { + {"get_signature", (PyCFunction)get_signature, METH_VARARGS, + "get the __signature__, but pass an optional string parameter"}, + {NULL, NULL} +}; + +static void +init_module_2(void) { - PyObject *arg_tup, *dict, *type_name; static int init_done = 0; if (!init_done) { - if (init_phase_2(pyside_globals) < 0) - return NULL; + // Phase 2 will call __init__.py which touches a signature, itself. + // Therefore we set init_done prior to init_phase_2(). init_done = 1; + init_phase_2(pyside_globals, signature_methods); } +} + +static PyObject * +PySide_BuildSignatureProps(PyObject *classmod) +{ /* * Here is the second part of the function. * This part will be called on-demand when needed by some attribute. * We simply pick up the arguments that we stored here and replace * them by the function result. */ - type_name = PyObject_GetAttrString(classmod, "__name__"); - if (type_name == NULL) - return NULL; - arg_tup = PyDict_GetItem(pyside_globals->arg_dict, type_name); - if (arg_tup == NULL) - return NULL; - dict = PyObject_CallObject(pyside_globals->sigparse_func, arg_tup); - if (dict == NULL) - return NULL; + init_module_2(); + Shiboken::AutoDecRef type_key(GetClassKey(classmod)); + if (type_key.isNull()) + return nullptr; + PyObject *arg_tup = PyDict_GetItem(pyside_globals->arg_dict, type_key); + if (arg_tup == nullptr) + return nullptr; + PyObject *dict = PyObject_CallObject(pyside_globals->sigparse_func, arg_tup); + if (dict == nullptr) { + if (PyErr_Occurred()) + return nullptr; + // No error: return an empty dict. + if (empty_dict == nullptr) + empty_dict = PyDict_New(); + return empty_dict; + } // We replace the arguments by the result dict. - if (PyDict_SetItem(pyside_globals->arg_dict, type_name, dict) < 0) - return NULL; - Py_DECREF(type_name); + if (PyDict_SetItem(pyside_globals->arg_dict, type_key, dict) < 0) + return nullptr; return dict; } @@ -638,7 +768,7 @@ SbkSpecial_Type_Ready(PyObject *module, PyTypeObject *type, const char *signatures) { int ret; - if (PySideType_Ready(type) < 0) + if (PyType_Ready(type) < 0) return -1; ret = PySide_BuildSignatureArgs(module, (PyObject *)type, signatures); if (ret < 0) { @@ -648,27 +778,25 @@ SbkSpecial_Type_Ready(PyObject *module, PyTypeObject *type, return ret; } +static int _finish_nested_classes(PyObject *dict); +static int _build_func_to_type(PyObject *obtype); + static int PySide_FinishSignatures(PyObject *module, const char *signatures) { - const char *name = NULL; + /* + * Initialization of module functions and resolving of static methods. + */ - // CRUCIAL: Do not call this on "testbinding": - // The module is different and should not get signatures, anyway. - name = PyModule_GetName(module); + const char *name = PyModule_GetName(module); if (name == NULL) return -1; - if (strncmp(name, "PySide2.Qt", 10) != 0) - return 0; // we abuse the call for types, since they both have a __name__ attribute. if (PySide_BuildSignatureArgs(module, module, signatures) < 0) return -1; /* - * Python2 does not abuse the 'm_self' field for the type. So we need to - * supply this for all static methods. - * * Note: This function crashed when called from PySide_BuildSignatureArgs. * Probably this was too early. * @@ -676,20 +804,111 @@ PySide_FinishSignatures(PyObject *module, const char *signatures) * to the PyCFunction attributes. Therefore I simplified things * and always use our own mapping. */ - { - PyObject *key, *value; - Py_ssize_t pos = 0; - PyObject *dict = PyModule_GetDict(module); + PyObject *key, *func, *obdict = PyModule_GetDict(module); + Py_ssize_t pos = 0; + + while (PyDict_Next(obdict, &pos, &key, &func)) + if (PyCFunction_Check(func)) + if (PyDict_SetItem(pyside_globals->map_dict, func, module) < 0) + return -1; + if (_finish_nested_classes(obdict) < 0) + return -1; + return 0; +} + +static int +_finish_nested_classes(PyObject *obdict) +{ + PyObject *key, *value, *obtype; + PyTypeObject *subtype; + Py_ssize_t pos = 0; + + if (obdict == NULL) + return -1; + while (PyDict_Next(obdict, &pos, &key, &value)) { + if (PyType_Check(value)) { + obtype = value; + if (_build_func_to_type(obtype) < 0) + return -1; + // now continue with nested cases + subtype = reinterpret_cast<PyTypeObject *>(obtype); + if (_finish_nested_classes(subtype->tp_dict) < 0) + return -1; + } + } + return 0; +} + +static int +_build_func_to_type(PyObject *obtype) +{ + /* + * There is no general way to directly get the type of a static method. + * On Python 3, the type is hidden in an unused pointer in the + * PyCFunction structure, but the Limited API does not allow to access + * this, either. + * + * In the end, it was easier to avoid such tricks and build an explicit + * mapping from function to type. + * + * We walk through the method list of the type + * and record the mapping from static method to this type in a dict. + * We also check for hidden methods, see below. + */ + PyTypeObject *type = reinterpret_cast<PyTypeObject *>(obtype); + PyObject *dict = type->tp_dict; + PyMethodDef *meth = type->tp_methods; - if (dict == NULL) + if (meth == 0) + return 0; + + for (; meth->ml_name != NULL; meth++) { + /* + * It is possible that a method is overwritten by another + * attribute with the same name. This case was obviously provoked + * explicitly in "testbinding.TestObject.staticMethodDouble", + * where instead of the method a "PySide2.QtCore.Signal" object + * was in the dict. + * This overlap is also found in regular PySide under + * "PySide2.QtCore.QProcess.error" where again a signal object is + * returned. These hidden methods will be opened for the + * signature module by adding them under the name + * "{name}.overload". + */ + PyObject *descr = PyDict_GetItemString(dict, meth->ml_name); + const char *look_attr = meth->ml_flags & METH_STATIC ? "__func__" : "__name__"; + int check_name = meth->ml_flags & METH_STATIC ? 0 : 1; + if (descr == NULL) return -1; - while (PyDict_Next(dict, &pos, &key, &value)) { - if (PyType_Check(value)) { - PyObject *type = value; - if (build_func_to_type(type) < 0) - return -1; - } + // We first check all methods if one is hidden by something else. + Shiboken::AutoDecRef look(PyObject_GetAttrString(descr, look_attr)); + Shiboken::AutoDecRef given(Py_BuildValue("s", meth->ml_name)); + if (look.isNull() + || (check_name && PyObject_RichCompareBool(look, given, Py_EQ) != 1)) { + PyErr_Clear(); + Shiboken::AutoDecRef cfunc(PyCFunction_NewEx(meth, (PyObject*)type, NULL)); + if (cfunc.isNull()) + return -1; + if (meth->ml_flags & METH_STATIC) + descr = PyStaticMethod_New(cfunc); + else + descr = PyDescr_NewMethod(type, meth); + if (descr == nullptr) + return -1; + char mangled_name[200]; + strcpy(mangled_name, meth->ml_name); + strcat(mangled_name, ".overload"); + if (PyDict_SetItemString(dict, mangled_name, descr) < 0) + return -1; + if (PyDict_SetItemString(pyside_globals->map_dict, mangled_name, obtype) < 0) + return -1; + continue; + } + // Then we insert the mapping for static methods. + if (meth->ml_flags & METH_STATIC) { + if (PyDict_SetItem(pyside_globals->map_dict, look, obtype) < 0) + return -1; } } return 0; @@ -698,6 +917,15 @@ PySide_FinishSignatures(PyObject *module, const char *signatures) void FinishSignatureInitialization(PyObject *module, const char *signatures) { + /* + * This function is called at the very end of a module initialization. + * We now patch certain types to support the __signature__ attribute, + * initialize module functions and resolve static methods. + * + * Still, it is not possible to call init phase 2 from here, + * because the import is still running. Do it from Python! + */ + PySide_PatchTypes(); if (PySide_FinishSignatures(module, signatures) < 0) { PyErr_Print(); PyErr_SetNone(PyExc_ImportError); diff --git a/sources/shiboken2/libshiboken/signature.h b/sources/shiboken2/libshiboken/signature.h index 3a229cb5c..b65317662 100644 --- a/sources/shiboken2/libshiboken/signature.h +++ b/sources/shiboken2/libshiboken/signature.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt for Python. @@ -45,8 +45,8 @@ extern "C" { -LIBSHIBOKEN_API int SbkSpecial_Type_Ready(PyObject *, PyTypeObject *, const char*); -LIBSHIBOKEN_API void FinishSignatureInitialization(PyObject *, const char*); +LIBSHIBOKEN_API int SbkSpecial_Type_Ready(PyObject *, PyTypeObject *, const char *); //WS +LIBSHIBOKEN_API void FinishSignatureInitialization(PyObject *, const char *); } // extern "C" diff --git a/sources/shiboken2/libshiboken/threadstatesaver.h b/sources/shiboken2/libshiboken/threadstatesaver.h index e9f97f300..ddad9b67f 100644 --- a/sources/shiboken2/libshiboken/threadstatesaver.h +++ b/sources/shiboken2/libshiboken/threadstatesaver.h @@ -49,15 +49,17 @@ namespace Shiboken class LIBSHIBOKEN_API ThreadStateSaver { public: + ThreadStateSaver(const ThreadStateSaver&) = delete; + ThreadStateSaver(ThreadStateSaver&&) = delete; + ThreadStateSaver &operator=(const ThreadStateSaver&) = delete; + ThreadStateSaver &operator=(ThreadStateSaver&&) = delete; + ThreadStateSaver(); ~ThreadStateSaver(); void save(); void restore(); private: PyThreadState* m_threadState; - - ThreadStateSaver(const ThreadStateSaver&); - ThreadStateSaver& operator=(const ThreadStateSaver&); }; } // namespace Shiboken diff --git a/sources/shiboken2/libshiboken/typespec.cpp b/sources/shiboken2/libshiboken/typespec.cpp index d532c97ed..a67daf12d 100644 --- a/sources/shiboken2/libshiboken/typespec.cpp +++ b/sources/shiboken2/libshiboken/typespec.cpp @@ -37,6 +37,7 @@ ** ****************************************************************************/ +#include "sbkpython.h" #include "typespec.h" #include <structmember.h> @@ -514,7 +515,7 @@ best_base(PyObject *bases) } static const short slotoffsets[] = { - -1, /* invalid slot_ */ + -1, /* invalid slot */ /* Generated by typeslots.py */ 0, 0, @@ -603,7 +604,7 @@ PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases) PyObject *modname; char *s; char *res_start = (char*)res; - PyType_Slot *slot_; + PyType_Slot *slot; /* Set the type name and qualname */ s = (char *)strrchr(spec->name, '.'); // C++11 @@ -632,11 +633,11 @@ PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases) if (!bases) { base = &PyBaseObject_Type; /* See whether Py_tp_base(s) was specified */ - for (slot_ = spec->slots; slot_->slot_; slot_++) { - if (slot_->slot_ == Py_tp_base) - base = (PyTypeObject *)slot_->pfunc; // C++11 - else if (slot_->slot_ == Py_tp_bases) { - bases = (PyObject *)slot_->pfunc; // C++11 + for (slot = spec->slots; slot->slot; slot++) { + if (slot->slot == Py_tp_base) + base = (PyTypeObject *)slot->pfunc; // C++11 + else if (slot->slot == Py_tp_bases) { + bases = (PyObject *)slot->pfunc; // C++11 Py_INCREF(bases); } } @@ -676,23 +677,23 @@ PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases) type->tp_basicsize = spec->basicsize; type->tp_itemsize = spec->itemsize; - for (slot_ = spec->slots; slot_->slot_; slot_++) { - if (slot_->slot_ < 0 - || (size_t)slot_->slot_ >= Py_ARRAY_LENGTH(slotoffsets)) { - PyErr_SetString(PyExc_RuntimeError, "invalid slot_ offset"); + for (slot = spec->slots; slot->slot; slot++) { + if (slot->slot < 0 + || (size_t)slot->slot >= Py_ARRAY_LENGTH(slotoffsets)) { + PyErr_SetString(PyExc_RuntimeError, "invalid slot offset"); goto fail; } - if (slot_->slot_ == Py_tp_base || slot_->slot_ == Py_tp_bases) + if (slot->slot == Py_tp_base || slot->slot == Py_tp_bases) /* Processed above */ continue; - *(void**)(res_start + slotoffsets[slot_->slot_]) = slot_->pfunc; + *(void**)(res_start + slotoffsets[slot->slot]) = slot->pfunc; - /* need to make a copy of the docstring slot_, which usually + /* need to make a copy of the docstring slot, which usually points to a static string literal */ - if (slot_->slot_ == Py_tp_doc) { + if (slot->slot == Py_tp_doc) { // No signature in Python 2 - // const char *old_doc = _PyType_DocWithoutSignature(type->tp_name, slot_->pfunc); - const char *old_doc = (const char *)slot_->pfunc; + // const char *old_doc = _PyType_DocWithoutSignature(type->tp_name, slot->pfunc); + const char *old_doc = (const char *)slot->pfunc; size_t len = strlen(old_doc)+1; char *tp_doc = (char *)PyObject_MALLOC(len); // C++11 if (tp_doc == NULL) { @@ -759,17 +760,17 @@ PyType_FromSpec(PyType_Spec *spec) } void * -PyType_GetSlot(PyTypeObject *type, int slot_) +PyType_GetSlot(PyTypeObject *type, int slot) { - if (!PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE) || slot_ < 0) { + if (!PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE) || slot < 0) { PyErr_BadInternalCall(); return NULL; } - if ((size_t)slot_ >= Py_ARRAY_LENGTH(slotoffsets)) { - /* Extension module requesting slot_ from a future version */ + if ((size_t)slot >= Py_ARRAY_LENGTH(slotoffsets)) { + /* Extension module requesting slot from a future version */ return NULL; } - return *(void**)(((char*)type) + slotoffsets[slot_]); + return *(void**)(((char*)type) + slotoffsets[slot]); } } // extern "C" diff --git a/sources/shiboken2/libshiboken/typespec.h b/sources/shiboken2/libshiboken/typespec.h index 799fcb1b8..81227acac 100644 --- a/sources/shiboken2/libshiboken/typespec.h +++ b/sources/shiboken2/libshiboken/typespec.h @@ -40,7 +40,7 @@ #ifndef TYPESPEC_H #define TYPESPEC_H -#include <Python.h> +#include "sbkpython.h" #include "shibokenmacros.h" #if PY_MAJOR_VERSION < 3 @@ -48,7 +48,7 @@ extern "C" { typedef struct{ - int slot_; // slot is somehow reserved in Qt /* slot id, see below */ + int slot; // slot is somehow reserved in Qt /* slot id, see below */ void *pfunc; /* function pointer */ } PyType_Slot; diff --git a/sources/shiboken2/libshiboken/voidptr.cpp b/sources/shiboken2/libshiboken/voidptr.cpp index 0d7b6b9cd..a306f7a9d 100644 --- a/sources/shiboken2/libshiboken/voidptr.cpp +++ b/sources/shiboken2/libshiboken/voidptr.cpp @@ -193,7 +193,11 @@ PyObject *SbkVoidPtrObject_repr(PyObject *v) SbkVoidPtrObject *sbkObject = reinterpret_cast<SbkVoidPtrObject *>(v); + #ifdef IS_PY3K + PyObject *s = PyUnicode_FromFormat("%s(%p, %zd, %s)", + #else PyObject *s = PyBytes_FromFormat("%s(%p, %zd, %s)", + #endif Py_TYPE(sbkObject)->tp_name, sbkObject->cptr, sbkObject->size, @@ -205,7 +209,11 @@ PyObject *SbkVoidPtrObject_repr(PyObject *v) PyObject *SbkVoidPtrObject_str(PyObject *v) { SbkVoidPtrObject *sbkObject = reinterpret_cast<SbkVoidPtrObject *>(v); + #ifdef IS_PY3K + PyObject *s = PyUnicode_FromFormat("%s(Address %p, Size %zd, isWritable %s)", + #else PyObject *s = PyBytes_FromFormat("%s(Address %p, Size %zd, isWritable %s)", + #endif Py_TYPE(sbkObject)->tp_name, sbkObject->cptr, sbkObject->size, diff --git a/sources/shiboken2/shiboken_tool.py b/sources/shiboken2/shiboken_tool.py new file mode 100755 index 000000000..8494c5d57 --- /dev/null +++ b/sources/shiboken2/shiboken_tool.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of Qt for Python. +## +## $QT_BEGIN_LICENSE:LGPL$ +## 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 Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## 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-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# +import sys +import os +import subprocess + +def main(): + # The tools listed as entrypoints in setup.py are copied to 'scripts/..' + cmd = os.path.join("..", os.path.basename(sys.argv[0])) + command = [os.path.join(os.path.dirname(os.path.realpath(__file__)), cmd)] + command.extend(sys.argv[1:]) + sys.exit(subprocess.call(command)) + +if __name__ == "__main__": + main() diff --git a/sources/shiboken2/shiboken_version.py b/sources/shiboken2/shiboken_version.py index 709dd8714..8df1c2992 100644 --- a/sources/shiboken2/shiboken_version.py +++ b/sources/shiboken2/shiboken_version.py @@ -38,8 +38,10 @@ ############################################################################# major_version = "5" -minor_version = "11" -patch_version = "4" +minor_version = "12" +patch_version = "0" +pre_release_version_type = "a" # e.g. "a", "b", "rc". +pre_release_version = "1" # e.g "1", "2", (which means "beta1", "beta2", if type is "b") # For example: "a", "b", "rc" # (which means "alpha", "beta", "release candidate"). diff --git a/sources/shiboken2/shibokenmodule/CMakeLists.txt b/sources/shiboken2/shibokenmodule/CMakeLists.txt index f2d7b30f2..0eba3eaff 100644 --- a/sources/shiboken2/shibokenmodule/CMakeLists.txt +++ b/sources/shiboken2/shibokenmodule/CMakeLists.txt @@ -12,7 +12,9 @@ set(shibokenmodule_TYPESYSTEM ${CMAKE_CURRENT_SOURCE_DIR}/typesystem_shiboken.xml ) -add_custom_command(OUTPUT ${sample_SRC} +add_custom_command( +OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/mjb_rejected_classes.log" +BYPRODUCTS ${sample_SRC} # Note: shiboken2 is an executable target. By not specifying its explicit # path, CMAKE figures it out, itself! # This fixes an issue with Visual Studio, see https://github.com/PySide/shiboken2/pull/11 @@ -39,5 +41,53 @@ target_link_libraries(shibokenmodule libshiboken) add_dependencies(shibokenmodule shiboken2) +create_generator_target(shibokenmodule) -install(TARGETS shibokenmodule DESTINATION ${PYTHON_SITE_PACKAGES}) +install(TARGETS shibokenmodule DESTINATION ${PYTHON_SITE_PACKAGES}/shiboken2) + +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/_config.py.in" + "${CMAKE_CURRENT_BINARY_DIR}/_config.py" @ONLY) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/_config.py" + DESTINATION "${PYTHON_SITE_PACKAGES}/shiboken2") + +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/__init__.py.in" + "${CMAKE_CURRENT_BINARY_DIR}/__init__.py" @ONLY) + +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/__init__.py" + "${CMAKE_CURRENT_BINARY_DIR}/support/__init__.py" COPYONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/__init__.py" + "${CMAKE_CURRENT_BINARY_DIR}/support/signature/__init__.py" COPYONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/layout.py" + "${CMAKE_CURRENT_BINARY_DIR}/support/signature/layout.py" COPYONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/loader.py" + "${CMAKE_CURRENT_BINARY_DIR}/support/signature/loader.py" COPYONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/mapping.py" + "${CMAKE_CURRENT_BINARY_DIR}/support/signature/mapping.py" COPYONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/parser.py" + "${CMAKE_CURRENT_BINARY_DIR}/support/signature/parser.py" COPYONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/lib/__init__.py" + "${CMAKE_CURRENT_BINARY_DIR}/support/signature/lib/__init__.py" COPYONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/lib/enum_sig.py" + "${CMAKE_CURRENT_BINARY_DIR}/support/signature/lib/enum_sig.py" COPYONLY) +if (PYTHON_VERSION_MAJOR EQUAL 3) +else() + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/backport_inspect.py" + "${CMAKE_CURRENT_BINARY_DIR}/support/signature/backport_inspect.py" COPYONLY) + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/typing27.py" + "${CMAKE_CURRENT_BINARY_DIR}/support/signature/typing27.py" COPYONLY) +endif() +install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/support" + DESTINATION "${PYTHON_SITE_PACKAGES}/shiboken2") + +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/__init__.py" + DESTINATION "${PYTHON_SITE_PACKAGES}/shiboken2") + +# Use absolute path instead of relative path, to avoid ninja build errors due to +# duplicate file dependency inconsistency. +set(shiboken_version_relative_path "${CMAKE_CURRENT_SOURCE_DIR}/../shiboken_version.py") +get_filename_component(shiboken_version_path ${shiboken_version_relative_path} ABSOLUTE) +configure_file("${shiboken_version_path}" + "${CMAKE_CURRENT_BINARY_DIR}/_git_shiboken_module_version.py" @ONLY) + +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/_git_shiboken_module_version.py" + DESTINATION "${PYTHON_SITE_PACKAGES}/shiboken2") diff --git a/sources/shiboken2/shibokenmodule/__init__.py.in b/sources/shiboken2/shibokenmodule/__init__.py.in new file mode 100644 index 000000000..81ab0063a --- /dev/null +++ b/sources/shiboken2/shibokenmodule/__init__.py.in @@ -0,0 +1,4 @@ +__version__ = "@FINAL_PACKAGE_VERSION@" +__version_info__ = (@shiboken_MAJOR_VERSION@, @shiboken_MINOR_VERSION@, @shiboken_MICRO_VERSION@, "@shiboken_PRE_RELEASE_VERSION_TYPE@", "@shiboken_PRE_RELEASE_VERSION@") + +from .shiboken2 import * diff --git a/sources/shiboken2/shibokenmodule/_config.py.in b/sources/shiboken2/shibokenmodule/_config.py.in new file mode 100644 index 000000000..9607e5ca7 --- /dev/null +++ b/sources/shiboken2/shibokenmodule/_config.py.in @@ -0,0 +1,11 @@ +shiboken_library_soversion = str(@shiboken2_library_so_version@) + +version = "@FINAL_PACKAGE_VERSION@" +version_info = (@shiboken_MAJOR_VERSION@, @shiboken_MINOR_VERSION@, @shiboken_MICRO_VERSION@, "@shiboken_PRE_RELEASE_VERSION_TYPE@", "@shiboken_PRE_RELEASE_VERSION@") + +@PACKAGE_BUILD_DATE@ +@PACKAGE_BUILD_COMMIT_DATE@ +@PACKAGE_BUILD_COMMIT_HASH@ +@PACKAGE_BUILD_COMMIT_HASH_DESCRIBED@ +@PACKAGE_SETUP_PY_PACKAGE_TIMESTAMP_ASSIGNMENT@ +@PACKAGE_SETUP_PY_PACKAGE_VERSION_ASSIGNMENT@ diff --git a/sources/shiboken2/shibokenmodule/support/__init__.py b/sources/shiboken2/shibokenmodule/support/__init__.py new file mode 100644 index 000000000..760d89571 --- /dev/null +++ b/sources/shiboken2/shibokenmodule/support/__init__.py @@ -0,0 +1,40 @@ +############################################################################# +## +## Copyright (C) 2017 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of Qt for Python. +## +## $QT_BEGIN_LICENSE:LGPL$ +## 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 Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## 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-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +# this file intentionally left blank diff --git a/sources/shiboken2/shibokenmodule/support/signature/PSF-3.7.0.txt b/sources/shiboken2/shibokenmodule/support/signature/PSF-3.7.0.txt new file mode 100644 index 000000000..be42010dd --- /dev/null +++ b/sources/shiboken2/shibokenmodule/support/signature/PSF-3.7.0.txt @@ -0,0 +1,43 @@ +PSF LICENSE AGREEMENT FOR PYTHON 3.7.0 + +1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and + the Individual or Organization ("Licensee") accessing and otherwise using Python + 3.7.0 software in source or binary form and its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF hereby + grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, + analyze, test, perform and/or display publicly, prepare derivative works, + distribute, and otherwise use Python 3.7.0 alone or in any derivative + version, provided, however, that PSF's License Agreement and PSF's notice of + copyright, i.e., "Copyright © 2001-2018 Python Software Foundation; All Rights + Reserved" are retained in Python 3.7.0 alone or in any derivative version + prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on or + incorporates Python 3.7.0 or any part thereof, and wants to make the + derivative work available to others as provided herein, then Licensee hereby + agrees to include in any such work a brief summary of the changes made to Python + 3.7.0. + +4. PSF is making Python 3.7.0 available to Licensee on an "AS IS" basis. + PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF + EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR + WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE + USE OF PYTHON 3.7.0 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 3.7.0 + FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF + MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 3.7.0, OR ANY DERIVATIVE + THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material breach of + its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any relationship + of agency, partnership, or joint venture between PSF and Licensee. This License + Agreement does not grant permission to use PSF trademarks or trade name in a + trademark sense to endorse or promote products or services of Licensee, or any + third party. + +8. By copying, installing or otherwise using Python 3.7.0, Licensee agrees + to be bound by the terms and conditions of this License Agreement. diff --git a/sources/shiboken2/shibokenmodule/support/signature/__init__.py b/sources/shiboken2/shibokenmodule/support/signature/__init__.py new file mode 100644 index 000000000..d0791df04 --- /dev/null +++ b/sources/shiboken2/shibokenmodule/support/signature/__init__.py @@ -0,0 +1,47 @@ +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of Qt for Python. +## +## $QT_BEGIN_LICENSE:LGPL$ +## 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 Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## 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-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +from __future__ import print_function, absolute_import + +# Trigger initialization phase 2. +_ = type.__signature__ + +## from signature_loader import get_signature, inspect, typing +# This causes a recursion in Python 2! +# We do everything from signature_loader, instead. diff --git a/sources/shiboken2/shibokenmodule/support/signature/backport_inspect.py b/sources/shiboken2/shibokenmodule/support/signature/backport_inspect.py new file mode 100644 index 000000000..6b97470e2 --- /dev/null +++ b/sources/shiboken2/shibokenmodule/support/signature/backport_inspect.py @@ -0,0 +1,956 @@ +# This Python file uses the following encoding: utf-8 +# It has been edited by fix-complaints.py . + +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of Qt for Python. +## +## $QT_BEGIN_LICENSE:LGPL$ +## 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 Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## 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-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +from __future__ import print_function + +""" +PSF LICENSE AGREEMENT FOR PYTHON 3.7.0 + +1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and + the Individual or Organization ("Licensee") accessing and otherwise using Python + 3.7.0 software in source or binary form and its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF hereby + grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, + analyze, test, perform and/or display publicly, prepare derivative works, + distribute, and otherwise use Python 3.7.0 alone or in any derivative + version, provided, however, that PSF's License Agreement and PSF's notice of + copyright, i.e., "Copyright © 2001-2018 Python Software Foundation; All Rights + Reserved" are retained in Python 3.7.0 alone or in any derivative version + prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on or + incorporates Python 3.7.0 or any part thereof, and wants to make the + derivative work available to others as provided herein, then Licensee hereby + agrees to include in any such work a brief summary of the changes made to Python + 3.7.0. + +4. PSF is making Python 3.7.0 available to Licensee on an "AS IS" basis. + PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF + EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR + WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE + USE OF PYTHON 3.7.0 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 3.7.0 + FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF + MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 3.7.0, OR ANY DERIVATIVE + THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material breach of + its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any relationship + of agency, partnership, or joint venture between PSF and Licensee. This License + Agreement does not grant permission to use PSF trademarks or trade name in a + trademark sense to endorse or promote products or services of Licensee, or any + third party. + +8. By copying, installing or otherwise using Python 3.7.0, Licensee agrees + to be bound by the terms and conditions of this License Agreement. +""" + +__doc__ = """ + signature() - get a Signature object for the callable +""" + +import sys +from collections import OrderedDict + +CO_OPTIMIZED = 0x0001 +CO_NEWLOCALS = 0x0002 +CO_VARARGS = 0x0004 +CO_VARKEYWORDS = 0x0008 +CO_NESTED = 0x0010 +CO_GENERATOR = 0x0020 +CO_NOFREE = 0x0040 + + +############################################################################### +### Function Signature Object (PEP 362) +############################################################################### + + +# This function was changed: 'builtins' and 'qualname' don't exist. +# We use '__builtin__' and '__name__' instead. +# It is further changed because we use a local copy of typing +def formatannotation(annotation, base_module=None): + if getattr(annotation, '__module__', None) == 'support.signature.typing': + return repr(annotation).replace('support.signature.typing', 'typing') + if isinstance(annotation, type): + if annotation.__module__ in ('__builtin__', base_module): + return annotation.__name__ + return annotation.__module__+'.'+annotation.__name__ + return repr(annotation) + + +def _signature_is_functionlike(obj): + """Private helper to test if `obj` is a duck type of FunctionType. + A good example of such objects are functions compiled with + Cython, which have all attributes that a pure Python function + would have, but have their code statically compiled. + """ + + if not callable(obj) or isclass(obj): + # All function-like objects are obviously callables, + # and not classes. + return False + + name = getattr(obj, '__name__', None) + code = getattr(obj, '__code__', None) + defaults = getattr(obj, '__defaults__', _void) # Important to use _void ... + kwdefaults = getattr(obj, '__kwdefaults__', _void) # ... and not None here + annotations = getattr(obj, '__annotations__', None) + + return (isinstance(code, types.CodeType) and + isinstance(name, str) and + (defaults is None or isinstance(defaults, tuple)) and + (kwdefaults is None or isinstance(kwdefaults, dict)) and + isinstance(annotations, dict)) + + + +def _signature_from_function(cls, func): + """Private helper: constructs Signature for the given python function.""" + + is_duck_function = False + if not isfunction(func): + if _signature_is_functionlike(func): + is_duck_function = True + else: + # If it's not a pure Python function, and not a duck type + # of pure function: + raise TypeError('{!r} is not a Python function'.format(func)) + + Parameter = cls._parameter_cls + + # Parameter information. + func_code = func.__code__ + pos_count = func_code.co_argcount + arg_names = func_code.co_varnames + positional = tuple(arg_names[:pos_count]) + keyword_only_count = 0 # func_code.co_kwonlyargcount + keyword_only = arg_names[pos_count:(pos_count + keyword_only_count)] + annotations = func.__annotations__ + defaults = func.__defaults__ + kwdefaults = func.__kwdefaults__ + + if defaults: + pos_default_count = len(defaults) + else: + pos_default_count = 0 + + parameters = [] + + # Non-keyword-only parameters w/o defaults. + non_default_count = pos_count - pos_default_count + for name in positional[:non_default_count]: + annotation = annotations.get(name, _empty) + parameters.append(Parameter(name, annotation=annotation, + kind=_POSITIONAL_OR_KEYWORD)) + + # ... w/ defaults. + for offset, name in enumerate(positional[non_default_count:]): + annotation = annotations.get(name, _empty) + parameters.append(Parameter(name, annotation=annotation, + kind=_POSITIONAL_OR_KEYWORD, + default=defaults[offset])) + + # *args + if func_code.co_flags & CO_VARARGS: + name = arg_names[pos_count + keyword_only_count] + annotation = annotations.get(name, _empty) + parameters.append(Parameter(name, annotation=annotation, + kind=_VAR_POSITIONAL)) + + # Keyword-only parameters. + for name in keyword_only: + default = _empty + if kwdefaults is not None: + default = kwdefaults.get(name, _empty) + + annotation = annotations.get(name, _empty) + parameters.append(Parameter(name, annotation=annotation, + kind=_KEYWORD_ONLY, + default=default)) + # **kwargs + if func_code.co_flags & CO_VARKEYWORDS: + index = pos_count + keyword_only_count + if func_code.co_flags & CO_VARARGS: + index += 1 + + name = arg_names[index] + annotation = annotations.get(name, _empty) + parameters.append(Parameter(name, annotation=annotation, + kind=_VAR_KEYWORD)) + + # Is 'func' is a pure Python function - don't validate the + # parameters list (for correct order and defaults), it should be OK. + return cls(parameters, + return_annotation=annotations.get('return', _empty), + __validate_parameters__=is_duck_function) + + + + +class _void(object): + """A private marker - used in Parameter & Signature.""" + + +class _empty(object): + """Marker object for Signature.empty and Parameter.empty.""" + + +class _ParameterKind(object): # (enum.IntEnum): + POSITIONAL_ONLY = 0 + POSITIONAL_OR_KEYWORD = 1 + VAR_POSITIONAL = 2 + KEYWORD_ONLY = 3 + VAR_KEYWORD = 4 + + def __str__(self): + return self._name_ + + +_POSITIONAL_ONLY = _ParameterKind.POSITIONAL_ONLY +_POSITIONAL_OR_KEYWORD = _ParameterKind.POSITIONAL_OR_KEYWORD +_VAR_POSITIONAL = _ParameterKind.VAR_POSITIONAL +_KEYWORD_ONLY = _ParameterKind.KEYWORD_ONLY +_VAR_KEYWORD = _ParameterKind.VAR_KEYWORD + + +class Parameter(object): + """Represents a parameter in a function signature. + + Has the following public attributes: + + * name : str + The name of the parameter as a string. + * default : object + The default value for the parameter if specified. If the + parameter has no default value, this attribute is set to + `Parameter.empty`. + * annotation + The annotation for the parameter if specified. If the + parameter has no annotation, this attribute is set to + `Parameter.empty`. + * kind : str + Describes how argument values are bound to the parameter. + Possible values: `Parameter.POSITIONAL_ONLY`, + `Parameter.POSITIONAL_OR_KEYWORD`, `Parameter.VAR_POSITIONAL`, + `Parameter.KEYWORD_ONLY`, `Parameter.VAR_KEYWORD`. + """ + + __slots__ = ('_name', '_kind', '_default', '_annotation') + + POSITIONAL_ONLY = _POSITIONAL_ONLY + POSITIONAL_OR_KEYWORD = _POSITIONAL_OR_KEYWORD + VAR_POSITIONAL = _VAR_POSITIONAL + KEYWORD_ONLY = _KEYWORD_ONLY + VAR_KEYWORD = _VAR_KEYWORD + + empty = _empty + + def __init__(self, name, kind, default=_empty, annotation=_empty): + + if kind not in (_POSITIONAL_ONLY, _POSITIONAL_OR_KEYWORD, + _VAR_POSITIONAL, _KEYWORD_ONLY, _VAR_KEYWORD): + raise ValueError("invalid value for 'Parameter.kind' attribute") + self._kind = kind + + if default is not _empty: + if kind in (_VAR_POSITIONAL, _VAR_KEYWORD): + msg = '{} parameters cannot have default values'.format(kind) + raise ValueError(msg) + self._default = default + self._annotation = annotation + + if name is _empty: + raise ValueError('name is a required attribute for Parameter') + + if not isinstance(name, str): + raise TypeError("name must be a str, not a {!r}".format(name)) + + if name[0] == '.' and name[1:].isdigit(): + # These are implicit arguments generated by comprehensions. In + # order to provide a friendlier interface to users, we recast + # their name as "implicitN" and treat them as positional-only. + # See issue 19611. + if kind != _POSITIONAL_OR_KEYWORD: + raise ValueError( + 'implicit arguments must be passed in as {}'.format( + _POSITIONAL_OR_KEYWORD + ) + ) + self._kind = _POSITIONAL_ONLY + name = 'implicit{}'.format(name[1:]) + + if not True: # name.isidentifier(): + raise ValueError('{!r} is not a valid parameter name'.format(name)) + + self._name = name + + def __reduce__(self): + return (type(self), + (self._name, self._kind), + {'_default': self._default, + '_annotation': self._annotation}) + + def __setstate__(self, state): + self._default = state['_default'] + self._annotation = state['_annotation'] + + @property + def name(self): + return self._name + + @property + def default(self): + return self._default + + @property + def annotation(self): + return self._annotation + + @property + def kind(self): + return self._kind + + def replace(self, name=_void, kind=_void, + annotation=_void, default=_void): + """Creates a customized copy of the Parameter.""" + + if name is _void: + name = self._name + + if kind is _void: + kind = self._kind + + if annotation is _void: + annotation = self._annotation + + if default is _void: + default = self._default + + return type(self)(name, kind, default=default, annotation=annotation) + + def __str__(self): + kind = self.kind + formatted = self._name + + # Add annotation and default value + if self._annotation is not _empty: + formatted = '{}:{}'.format(formatted, + formatannotation(self._annotation)) + + if self._default is not _empty: + formatted = '{}={}'.format(formatted, repr(self._default)) + + if kind == _VAR_POSITIONAL: + formatted = '*' + formatted + elif kind == _VAR_KEYWORD: + formatted = '**' + formatted + + return formatted + + def __repr__(self): + return '<{} "{}">'.format(self.__class__.__name__, self) + + def __hash__(self): + return hash((self.name, self.kind, self.annotation, self.default)) + + def __eq__(self, other): + if self is other: + return True + if not isinstance(other, Parameter): + return NotImplemented + return (self._name == other._name and + self._kind == other._kind and + self._default == other._default and + self._annotation == other._annotation) + + +class BoundArguments(object): + """Result of `Signature.bind` call. Holds the mapping of arguments + to the function's parameters. + + Has the following public attributes: + + * arguments : OrderedDict + An ordered mutable mapping of parameters' names to arguments' values. + Does not contain arguments' default values. + * signature : Signature + The Signature object that created this instance. + * args : tuple + Tuple of positional arguments values. + * kwargs : dict + Dict of keyword arguments values. + """ + + __slots__ = ('arguments', '_signature', '__weakref__') + + def __init__(self, signature, arguments): + self.arguments = arguments + self._signature = signature + + @property + def signature(self): + return self._signature + + @property + def args(self): + args = [] + for param_name, param in self._signature.parameters.items(): + if param.kind in (_VAR_KEYWORD, _KEYWORD_ONLY): + break + + try: + arg = self.arguments[param_name] + except KeyError: + # We're done here. Other arguments + # will be mapped in 'BoundArguments.kwargs' + break + else: + if param.kind == _VAR_POSITIONAL: + # *args + args.extend(arg) + else: + # plain argument + args.append(arg) + + return tuple(args) + + @property + def kwargs(self): + kwargs = {} + kwargs_started = False + for param_name, param in self._signature.parameters.items(): + if not kwargs_started: + if param.kind in (_VAR_KEYWORD, _KEYWORD_ONLY): + kwargs_started = True + else: + if param_name not in self.arguments: + kwargs_started = True + continue + + if not kwargs_started: + continue + + try: + arg = self.arguments[param_name] + except KeyError: + pass + else: + if param.kind == _VAR_KEYWORD: + # **kwargs + kwargs.update(arg) + else: + # plain keyword argument + kwargs[param_name] = arg + + return kwargs + + def apply_defaults(self): + """Set default values for missing arguments. + + For variable-positional arguments (*args) the default is an + empty tuple. + + For variable-keyword arguments (**kwargs) the default is an + empty dict. + """ + arguments = self.arguments + new_arguments = [] + for name, param in self._signature.parameters.items(): + try: + new_arguments.append((name, arguments[name])) + except KeyError: + if param.default is not _empty: + val = param.default + elif param.kind is _VAR_POSITIONAL: + val = () + elif param.kind is _VAR_KEYWORD: + val = {} + else: + # This BoundArguments was likely produced by + # Signature.bind_partial(). + continue + new_arguments.append((name, val)) + self.arguments = OrderedDict(new_arguments) + + def __eq__(self, other): + if self is other: + return True + if not isinstance(other, BoundArguments): + return NotImplemented + return (self.signature == other.signature and + self.arguments == other.arguments) + + def __setstate__(self, state): + self._signature = state['_signature'] + self.arguments = state['arguments'] + + def __getstate__(self): + return {'_signature': self._signature, 'arguments': self.arguments} + + def __repr__(self): + args = [] + for arg, value in self.arguments.items(): + args.append('{}={!r}'.format(arg, value)) + return '<{} ({})>'.format(self.__class__.__name__, ', '.join(args)) + + +class Signature(object): + """A Signature object represents the overall signature of a function. + It stores a Parameter object for each parameter accepted by the + function, as well as information specific to the function itself. + + A Signature object has the following public attributes and methods: + + * parameters : OrderedDict + An ordered mapping of parameters' names to the corresponding + Parameter objects (keyword-only arguments are in the same order + as listed in `code.co_varnames`). + * return_annotation : object + The annotation for the return type of the function if specified. + If the function has no annotation for its return type, this + attribute is set to `Signature.empty`. + * bind(*args, **kwargs) -> BoundArguments + Creates a mapping from positional and keyword arguments to + parameters. + * bind_partial(*args, **kwargs) -> BoundArguments + Creates a partial mapping from positional and keyword arguments + to parameters (simulating 'functools.partial' behavior.) + """ + + __slots__ = ('_return_annotation', '_parameters') + + _parameter_cls = Parameter + _bound_arguments_cls = BoundArguments + + empty = _empty + + def __init__(self, parameters=None, return_annotation=_empty, + __validate_parameters__=True): + """Constructs Signature from the given list of Parameter + objects and 'return_annotation'. All arguments are optional. + """ + + if parameters is None: + params = OrderedDict() + else: + if __validate_parameters__: + params = OrderedDict() + top_kind = _POSITIONAL_ONLY + kind_defaults = False + + for idx, param in enumerate(parameters): + kind = param.kind + name = param.name + + if kind < top_kind: + msg = 'wrong parameter order: {!r} before {!r}' + msg = msg.format(top_kind, kind) + raise ValueError(msg) + elif kind > top_kind: + kind_defaults = False + top_kind = kind + + if kind in (_POSITIONAL_ONLY, _POSITIONAL_OR_KEYWORD): + if param.default is _empty: + if kind_defaults: + # No default for this parameter, but the + # previous parameter of the same kind had + # a default + msg = 'non-default argument follows default ' \ + 'argument' + raise ValueError(msg) + else: + # There is a default for this parameter. + kind_defaults = True + + if name in params: + msg = 'duplicate parameter name: {!r}'.format(name) + raise ValueError(msg) + + params[name] = param + else: + params = OrderedDict(((param.name, param) + for param in parameters)) + + self._parameters = params # types.MappingProxyType(params) + self._return_annotation = return_annotation + + @classmethod + def from_function(cls, func): + """Constructs Signature for the given python function.""" + + warnings.warn("inspect.Signature.from_function() is deprecated, " + "use Signature.from_callable()", + DeprecationWarning, stacklevel=2) + return _signature_from_function(cls, func) + + @classmethod + def from_builtin(cls, func): + """Constructs Signature for the given builtin function.""" + + warnings.warn("inspect.Signature.from_builtin() is deprecated, " + "use Signature.from_callable()", + DeprecationWarning, stacklevel=2) + return _signature_from_builtin(cls, func) + + @classmethod + def from_callable(cls, obj, follow_wrapped=True): + """Constructs Signature for the given callable object.""" + return _signature_from_callable(obj, sigcls=cls, + follow_wrapper_chains=follow_wrapped) + + @property + def parameters(self): + return self._parameters + + @property + def return_annotation(self): + return self._return_annotation + + def replace(self, parameters=_void, return_annotation=_void): + """Creates a customized copy of the Signature. + Pass 'parameters' and/or 'return_annotation' arguments + to override them in the new copy. + """ + + if parameters is _void: + parameters = self.parameters.values() + + if return_annotation is _void: + return_annotation = self._return_annotation + + return type(self)(parameters, + return_annotation=return_annotation) + + def _hash_basis(self): + params = tuple(param for param in self.parameters.values() + if param.kind != _KEYWORD_ONLY) + + kwo_params = {param.name: param for param in self.parameters.values() + if param.kind == _KEYWORD_ONLY} + + return params, kwo_params, self.return_annotation + + def __hash__(self): + params, kwo_params, return_annotation = self._hash_basis() + kwo_params = frozenset(kwo_params.values()) + return hash((params, kwo_params, return_annotation)) + + def __eq__(self, other): + if self is other: + return True + if not isinstance(other, Signature): + return NotImplemented + return self._hash_basis() == other._hash_basis() + + def _bind(self, args, kwargs, partial=False): + """Private method. Don't use directly.""" + + arguments = OrderedDict() + + parameters = iter(self.parameters.values()) + parameters_ex = () + arg_vals = iter(args) + + while True: + # Let's iterate through the positional arguments and corresponding + # parameters + try: + arg_val = next(arg_vals) + except StopIteration: + # No more positional arguments + try: + param = next(parameters) + except StopIteration: + # No more parameters. That's it. Just need to check that + # we have no `kwargs` after this while loop + break + else: + if param.kind == _VAR_POSITIONAL: + # That's OK, just empty *args. Let's start parsing + # kwargs + break + elif param.name in kwargs: + if param.kind == _POSITIONAL_ONLY: + msg = '{arg!r} parameter is positional only, ' \ + 'but was passed as a keyword' + msg = msg.format(arg=param.name) + raise TypeError(msg)# from None + parameters_ex = (param,) + break + elif (param.kind == _VAR_KEYWORD or + param.default is not _empty): + # That's fine too - we have a default value for this + # parameter. So, lets start parsing `kwargs`, starting + # with the current parameter + parameters_ex = (param,) + break + else: + # No default, not VAR_KEYWORD, not VAR_POSITIONAL, + # not in `kwargs` + if partial: + parameters_ex = (param,) + break + else: + msg = 'missing a required argument: {arg!r}' + msg = msg.format(arg=param.name) + raise TypeError(msg)# from None + else: + # We have a positional argument to process + try: + param = next(parameters) + except StopIteration: + raise TypeError('too many positional arguments')# from None + else: + if param.kind in (_VAR_KEYWORD, _KEYWORD_ONLY): + # Looks like we have no parameter for this positional + # argument + raise TypeError( + 'too many positional arguments')# from None + + if param.kind == _VAR_POSITIONAL: + # We have an '*args'-like argument, let's fill it with + # all positional arguments we have left and move on to + # the next phase + values = [arg_val] + values.extend(arg_vals) + arguments[param.name] = tuple(values) + break + + if param.name in kwargs: + raise TypeError( + 'multiple values for argument {arg!r}'.format( + arg=param.name))# from None + + arguments[param.name] = arg_val + + # Now, we iterate through the remaining parameters to process + # keyword arguments + kwargs_param = None + for param in itertools.chain(parameters_ex, parameters): + if param.kind == _VAR_KEYWORD: + # Memorize that we have a '**kwargs'-like parameter + kwargs_param = param + continue + + if param.kind == _VAR_POSITIONAL: + # Named arguments don't refer to '*args'-like parameters. + # We only arrive here if the positional arguments ended + # before reaching the last parameter before *args. + continue + + param_name = param.name + try: + arg_val = kwargs.pop(param_name) + except KeyError: + # We have no value for this parameter. It's fine though, + # if it has a default value, or it is an '*args'-like + # parameter, left alone by the processing of positional + # arguments. + if (not partial and param.kind != _VAR_POSITIONAL and + param.default is _empty): + raise TypeError('missing a required argument: {arg!r}'. \ + format(arg=param_name))# from None + + else: + if param.kind == _POSITIONAL_ONLY: + # This should never happen in case of a properly built + # Signature object (but let's have this check here + # to ensure correct behavior just in case) + raise TypeError('{arg!r} parameter is positional only, ' + 'but was passed as a keyword'. \ + format(arg=param.name)) + + arguments[param_name] = arg_val + + if kwargs: + if kwargs_param is not None: + # Process our '**kwargs'-like parameter + arguments[kwargs_param.name] = kwargs + else: + raise TypeError( + 'got an unexpected keyword argument {arg!r}'.format( + arg=next(iter(kwargs)))) + + return self._bound_arguments_cls(self, arguments) + + def bind(*args, **kwargs): + """Get a BoundArguments object, that maps the passed `args` + and `kwargs` to the function's signature. Raises `TypeError` + if the passed arguments can not be bound. + """ + return args[0]._bind(args[1:], kwargs) + + def bind_partial(*args, **kwargs): + """Get a BoundArguments object, that partially maps the + passed `args` and `kwargs` to the function's signature. + Raises `TypeError` if the passed arguments can not be bound. + """ + return args[0]._bind(args[1:], kwargs, partial=True) + + def __reduce__(self): + return (type(self), + (tuple(self._parameters.values()),), + {'_return_annotation': self._return_annotation}) + + def __setstate__(self, state): + self._return_annotation = state['_return_annotation'] + + def __repr__(self): + return '<{} {}>'.format(self.__class__.__name__, self) + + def __str__(self): + result = [] + render_pos_only_separator = False + render_kw_only_separator = True + for param in self.parameters.values(): + formatted = str(param) + + kind = param.kind + + if kind == _POSITIONAL_ONLY: + render_pos_only_separator = True + elif render_pos_only_separator: + # It's not a positional-only parameter, and the flag + # is set to 'True' (there were pos-only params before.) + result.append('/') + render_pos_only_separator = False + + if kind == _VAR_POSITIONAL: + # OK, we have an '*args'-like parameter, so we won't need + # a '*' to separate keyword-only arguments + render_kw_only_separator = False + elif kind == _KEYWORD_ONLY and render_kw_only_separator: + # We have a keyword-only parameter to render and we haven't + # rendered an '*args'-like parameter before, so add a '*' + # separator to the parameters list ("foo(arg1, *, arg2)" case) + result.append('*') + # This condition should be only triggered once, so + # reset the flag + render_kw_only_separator = False + + result.append(formatted) + + if render_pos_only_separator: + # There were only positional-only parameters, hence the + # flag was not reset to 'False' + result.append('/') + + rendered = '({})'.format(', '.join(result)) + + if self.return_annotation is not _empty: + anno = formatannotation(self.return_annotation) + rendered += ' -> {}'.format(anno) + + return rendered + + +def signature(obj, follow_wrapped=True): + """Get a signature object for the passed callable.""" + return Signature.from_callable(obj, follow_wrapped=follow_wrapped) + + +def _main(): + """ Logic for inspecting an object given at command line """ + import argparse + import importlib + + parser = argparse.ArgumentParser() + parser.add_argument( + 'object', + help="The object to be analysed. " + "It supports the 'module:qualname' syntax") + parser.add_argument( + '-d', '--details', action='store_true', + help='Display info about the module rather than its source code') + + args = parser.parse_args() + + target = args.object + mod_name, has_attrs, attrs = target.partition(":") + try: + obj = module = importlib.import_module(mod_name) + except Exception as exc: + msg = "Failed to import {} ({}: {})".format(mod_name, + type(exc).__name__, + exc) + print(msg, file=sys.stderr) + exit(2) + + if has_attrs: + parts = attrs.split(".") + obj = module + for part in parts: + obj = getattr(obj, part) + + if module.__name__ in sys.builtin_module_names: + print("Can't get info for builtin modules.", file=sys.stderr) + exit(1) + + if args.details: + print('Target: {}'.format(target)) + print('Origin: {}'.format(getsourcefile(module))) + print('Cached: {}'.format(module.__cached__)) + if obj is module: + print('Loader: {}'.format(repr(module.__loader__))) + if hasattr(module, '__path__'): + print('Submodule search path: {}'.format(module.__path__)) + else: + try: + __, lineno = findsource(obj) + except Exception: + pass + else: + print('Line: {}'.format(lineno)) + + print('\n') + else: + print(getsource(obj)) + + +if __name__ == "__main__": + _main() diff --git a/sources/shiboken2/shibokenmodule/support/signature/fix-complaints.py b/sources/shiboken2/shibokenmodule/support/signature/fix-complaints.py new file mode 100644 index 000000000..e078ef1ab --- /dev/null +++ b/sources/shiboken2/shibokenmodule/support/signature/fix-complaints.py @@ -0,0 +1,91 @@ +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of Qt for Python. +## +## $QT_BEGIN_LICENSE:LGPL$ +## 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 Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## 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-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +from __future__ import print_function, absolute_import + +""" +fix-complaints.py + +This module fixes the buildbot messages of external python modules. +Run it once after copying a new version. It is idem-potent, unless +you are changing messages (what I did, of course :-) . +""" + +import os + +patched_modules = "backport_inspect typing27" + +offending_words = { + "behavio""ur": "behavior", + "at""least": "at_least", + "reali""sed": "realized", +} + +utf8_line = "# This Python file uses the following encoding: utf-8\n" +marker_line = "# It has been edited by {} .\n".format( + os.path.basename(__file__)) + +def patch_file(fname): + with open(fname) as f: + lines = f.readlines() + dup = lines[:] + for idx, line in enumerate(lines): + for word, repl in offending_words.items(): + if word in line: + lines[idx] = line.replace(word, repl) + print("line:{!r} {!r}->{!r}".format(line, word, repl)) + if lines[0].strip() != utf8_line.strip(): + lines[:0] = [utf8_line, "\n"] + if lines[1] != marker_line: + lines[1:1] = marker_line + if lines != dup: + with open(fname, "w") as f: + f.write("".join(lines)) + +def doit(): + dir = os.path.dirname(__file__) + for name in patched_modules.split(): + fname = os.path.join(dir, name + ".py") + print("Working on", fname) + patch_file(fname) + +if __name__ == "__main__": + doit() + +# end of file diff --git a/sources/shiboken2/shibokenmodule/support/signature/layout.py b/sources/shiboken2/shibokenmodule/support/signature/layout.py new file mode 100644 index 000000000..cd3a5dc8f --- /dev/null +++ b/sources/shiboken2/shibokenmodule/support/signature/layout.py @@ -0,0 +1,246 @@ +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of Qt for Python. +## +## $QT_BEGIN_LICENSE:LGPL$ +## 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 Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## 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-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +from __future__ import print_function, absolute_import + +""" +layout.py + +The signature module now has the capability to configure +differently formatted versions of signatures. The default +layout is known from the "__signature__" attribute. + +The function "get_signature(ob, modifier=None)" produces the same +signatures by default. By passing different modifiers, you +can select different layouts. + +This module configures the different layouts which can be used. +It also implements them in this file. The configurations are +used literally as strings like "signature", "existence", etc. +""" + +from textwrap import dedent +from signature_loader import inspect +from signature_loader.mapping import ellipsis + + +class SimpleNamespace(object): + # From types.rst, because the builtin is implemented in Python 3, only. + def __init__(self, **kwargs): + self.__dict__.update(kwargs) + + def __repr__(self): + keys = sorted(self.__dict__) + items = ("{}={!r}".format(k, self.__dict__[k]) for k in keys) + return "{}({})".format(type(self).__name__, ", ".join(items)) + + def __eq__(self, other): + return self.__dict__ == other.__dict__ + + +class SignatureLayout(SimpleNamespace): + """ + Configure a signature. + + The layout of signatures can have different layouts which are + controlled by keyword arguments: + + definition=True Determines if self will generated. + defaults=True + ellipsis=False Replaces defaults by "...". + return_annotation=True + parameter_names=True False removes names before ":". + """ + allowed_keys = SimpleNamespace(definition=True, + defaults=True, + ellipsis=False, + return_annotation=True, + parameter_names=True) + allowed_values = True, False + + def __init__(self, **kwds): + args = SimpleNamespace(**self.allowed_keys.__dict__) + args.__dict__.update(kwds) + self.__dict__.update(args.__dict__) + err_keys = list(set(self.__dict__) - set(self.allowed_keys.__dict__)) + if err_keys: + self._attributeerror(err_keys) + err_values = list(set(self.__dict__.values()) - set(self.allowed_values)) + if err_values: + self._valueerror(err_values) + + def __setattr__(self, key, value): + if key not in self.allowed_keys.__dict__: + self._attributeerror([key]) + if value not in self.allowed_values: + self._valueerror([value]) + self.__dict__[key] = value + + def _attributeerror(self, err_keys): + err_keys = ", ".join(err_keys) + allowed_keys = ", ".join(self.allowed_keys.__dict__.keys()) + raise AttributeError(dedent("""\ + Not allowed: '{err_keys}'. + The only allowed keywords are '{allowed_keys}'. + """.format(**locals()))) + + def _valueerror(self, err_values): + err_values = ", ".join(map(str, err_values)) + allowed_values = ", ".join(map(str, self.allowed_values)) + raise ValueError(dedent("""\ + Not allowed: '{err_values}'. + The only allowed values are '{allowed_values}'. + """.format(**locals()))) + +# The following names are used literally in this module. +# This way, we avoid the dict hashing problem. +signature = SignatureLayout() + +existence = SignatureLayout(definition=False, + defaults=False, + return_annotation=False, + parameter_names=False) + +hintingstub = SignatureLayout(ellipsis=True) + +typeerror = SignatureLayout(definition=False, + return_annotation=False, + parameter_names=False) + + +def define_nameless_parameter(): + """ + Create Nameless Parameters + + A nameless parameter has a reduced string representation. + This is done by cloning the parameter type and overwriting its + __str__ method. The inner structure is still a valid parameter. + """ + def __str__(self): + # for Python 2, we must change self to be an instance of P + klass = self.__class__ + self.__class__ = P + txt = P.__str__(self) + self.__class__ = klass + txt = txt[txt.index(":") + 1:].strip() if ":" in txt else txt + return txt + + P = inspect.Parameter + newname = "NamelessParameter" + bases = P.__bases__ + body = dict(P.__dict__) # get rid of mappingproxy + if "__slots__" in body: + # __slots__ would create duplicates + for name in body["__slots__"]: + del body[name] + body["__str__"] = __str__ + return type(newname, bases, body) + + +NamelessParameter = define_nameless_parameter() + + +def make_signature_nameless(signature): + """ + Make a Signature Nameless + + We use an existing signature and change the type of its parameters. + The signature looks different, but is totally intact. + """ + for key in signature.parameters.keys(): + signature.parameters[key].__class__ = NamelessParameter + + +def create_signature(props, key): + if not props: + # empty signatures string + return + if isinstance(props["multi"], list): + # multi sig: call recursively + return list(create_signature(elem, key) + for elem in props["multi"]) + if type(key) is tuple: + sig_kind, modifier = key + else: + sig_kind, modifier = key, "signature" + + layout = globals()[modifier] # lookup of the modifier in this module + if not isinstance(layout, SignatureLayout): + raise SystemError("Modifiers must be names of a SignatureLayout " + "instance") + + # this is the basic layout of a signature + varnames = props["varnames"] + if layout.definition: + if sig_kind == "function": + pass + elif sig_kind == "method": + varnames = ("self",) + varnames + elif sig_kind == "staticmethod": + pass + elif sig_kind == "classmethod": + varnames = ("klass",) + varnames + else: + raise SystemError("Methods must be function, method, staticmethod or " + "classmethod") + # calculate the modifications + defaults = props["defaults"][:] + if not layout.defaults: + defaults = () + if layout.ellipsis: + defaults = (ellipsis,) * len(defaults) + annotations = props["annotations"].copy() + if not layout.return_annotation and "return" in annotations: + del annotations["return"] + + # attach parameters to a fake function and build a signature + argstr = ", ".join(varnames) + fakefunc = eval("lambda {}: None".format(argstr)) + fakefunc.__name__ = props["name"] + fakefunc.__defaults__ = defaults + fakefunc.__kwdefaults__ = props["kwdefaults"] + fakefunc.__annotations__ = annotations + sig = inspect._signature_from_function(inspect.Signature, fakefunc) + + # the special case of nameless parameters + if not layout.parameter_names: + make_signature_nameless(sig) + return sig + +# end of file diff --git a/sources/shiboken2/shibokenmodule/support/signature/lib/__init__.py b/sources/shiboken2/shibokenmodule/support/signature/lib/__init__.py new file mode 100644 index 000000000..2d640cb89 --- /dev/null +++ b/sources/shiboken2/shibokenmodule/support/signature/lib/__init__.py @@ -0,0 +1,40 @@ +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of Qt for Python. +## +## $QT_BEGIN_LICENSE:LGPL$ +## 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 Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## 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-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +# this file intentionally left blank diff --git a/sources/shiboken2/shibokenmodule/support/signature/lib/enum_sig.py b/sources/shiboken2/shibokenmodule/support/signature/lib/enum_sig.py new file mode 100644 index 000000000..013ec36cc --- /dev/null +++ b/sources/shiboken2/shibokenmodule/support/signature/lib/enum_sig.py @@ -0,0 +1,168 @@ +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of Qt for Python. +## +## $QT_BEGIN_LICENSE:LGPL$ +## 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 Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## 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-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +from __future__ import print_function, absolute_import + +""" +enum_sig.py + +Enumerate all signatures of a class. + +This module separates the enumeration process from the formatting. +It is not easy to adhere to this protocol, but in the end, it paid off +by producing a lot of clarity. +""" + +import sys +from signature_loader import get_signature, inspect + + +class ExactEnumerator(object): + """ + ExactEnumerator enumerates all signatures in a module as they are. + + This class is used for generating complete listings of all signatures. + An appropriate formatter should be supplied, if printable output + is desired. + """ + + def __init__(self, formatter, result_type=dict): + self.fmt = formatter + self.result_type = result_type + + def module(self, mod_name): + __import__(mod_name) + with self.fmt.module(mod_name): + module = sys.modules[mod_name] + members = inspect.getmembers(module, inspect.isclass) + functions = inspect.getmembers(module, inspect.isroutine) + ret = self.result_type() + self.fmt.class_name = None + for func_name, func in functions: + ret.update(self.function(func_name, func)) + for class_name, klass in members: + ret.update(self.klass(class_name, klass)) + return ret + + def klass(self, class_name, klass): + if not "Shiboken" in repr(klass.mro()): + # don't look into any foreign classes! + ret = self.result_type() + return ret + bases_list = [] + for base in klass.__bases__: + name = base.__name__ + if name == "object": + pass + else: + modname = base.__module__ + name = modname + "." + base.__name__ + bases_list.append(name) + class_str = "{}({})".format(class_name, ", ".join(bases_list)) + with self.fmt.klass(class_name, class_str): + ret = self.function("__init__", klass) + # class_members = inspect.getmembers(klass) + # gives us also the inherited things. + class_members = sorted(list(klass.__dict__.items())) + subclasses = [] + for thing_name, thing in class_members: + if inspect.isclass(thing): + subclass_name = ".".join((class_name, thing_name)) + subclasses.append((subclass_name, thing)) + else: + func_name = thing_name.split(".")[0] # remove ".overload" + ret.update(self.function(func_name, thing)) + for subclass_name, subclass in subclasses: + ret.update(self.klass(subclass_name, subclass)) + return ret + + def function(self, func_name, func): + ret = self.result_type() + signature = getattr(func, '__signature__', None) + if signature is not None: + with self.fmt.function(func_name, signature) as key: + ret[key] = signature + return ret + + +def stringify(signature): + if isinstance(signature, list): + # remove duplicates which still sometimes occour: + ret = set(stringify(sig) for sig in signature) + return sorted(ret) if len(ret) > 1 else list(ret)[0] + return tuple(str(pv) for pv in signature.parameters.values()) + + +class SimplifyingEnumerator(ExactEnumerator): + """ + SimplifyingEnumerator enumerates all signatures in a module filtered. + + There are no default values, no variable + names and no self parameter. Only types are present after simplification. + The functions 'next' resp. '__next__' are removed + to make the output identical for Python 2 and 3. + An appropriate formatter should be supplied, if printable output + is desired. + """ + + def function(self, func_name, func): + ret = self.result_type() + signature = get_signature(func, 'existence') + sig = stringify(signature) if signature is not None else None + if sig is not None and func_name not in ("next", "__next__", "__div__"): + with self.fmt.function(func_name, sig) as key: + ret[key] = sig + return ret + +class HintingEnumerator(ExactEnumerator): + """ + HintingEnumerator enumerates all signatures in a module slightly changed. + + This class is used for generating complete listings of all signatures for + hinting stubs. Only default values are replaced by "...". + """ + + def function(self, func_name, func): + ret = self.result_type() + signature = get_signature(func, 'hintingstub') + if signature is not None: + with self.fmt.function(func_name, signature) as key: + ret[key] = signature + return ret + diff --git a/sources/shiboken2/shibokenmodule/support/signature/loader.py b/sources/shiboken2/shibokenmodule/support/signature/loader.py new file mode 100644 index 000000000..de27d441c --- /dev/null +++ b/sources/shiboken2/shibokenmodule/support/signature/loader.py @@ -0,0 +1,202 @@ +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of Qt for Python. +## +## $QT_BEGIN_LICENSE:LGPL$ +## 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 Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## 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-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +from __future__ import print_function, absolute_import + +""" +loader.py + +The loader has to lazy-load the signature module and also provides a few +Python modules to support Python 2.7 . + +This file was originally directly embedded into the C source. +After it grew more and more, I now prefer to have it as Python file. +The remaining stub loader in the C source is now only a short string. + +This version does no longer use an embedded .zip file but is a package. +The old code without a package but with zip compression can still be found +at https://codereview.qt-project.org/#/c/203533/ for reference. +""" + +import sys +import os +import traceback +import types +from contextlib import contextmanager + +""" +A note on the import problem (solved): + +During the tests, the shiboken build structure has the layout + + shiboken2/shibokenmodule/shiboken2.abi3.so + +and the name "shiboken2" in sys.modules points directly to the binary +file, hiding the outer shiboken2 module. + +To fix that, we temporarily remove the binary from sys.path, +do the needed imports and then restore the binary. +This action was put into a context manager for readability. +""" + +# On Python 2, we only have ImportError, which is way too coarse. +# When problems occour, please use Python 3, because it has the finer +# ModuleNotFoundError. + +try: + ModuleNotFoundError +except NameError: + ModuleNotFoundError = ImportError + +@contextmanager +def ensure_import_support(): + # Make sure that we always have the shiboken containing package first. + # This is sometimes hidden by the ctest paths. + # We adjust the path in a way that the support folder comes first. + # This can be in "shiboken2/support" or in "shibokenmodule/support", + # so we use the "support" folder as toplevel. + sbk_support_dir = os.path.abspath(os.path.join(__file__, "..", "..", "..")) + sys.path.insert(0, sbk_support_dir) + sbk = "shiboken2" + save_sbk = sys.modules.pop(sbk) if sbk in sys.modules else None + # make sure that we get at the support folder + try: + import support + yield + except Exception as e: + print("Problem importing support:") + print(e) + traceback.print_exc() + sys.stdout.flush() + sys.exit(-1) + if save_sbk: + sys.modules[sbk] = save_sbk + sys.path.pop(0) + + +# patching inspect's formatting to keep the word "typing": +def formatannotation(annotation, base_module=None): + # if getattr(annotation, '__module__', None) == 'typing': + # return repr(annotation).replace('typing.', '') + if isinstance(annotation, type): + if annotation.__module__ in ('builtins', base_module): + return annotation.__qualname__ + return annotation.__module__+'.'+annotation.__qualname__ + return repr(annotation) + +# patching __repr__ to disable the __repr__ of typing.TypeVar: +""" + def __repr__(self): + if self.__covariant__: + prefix = '+' + elif self.__contravariant__: + prefix = '-' + else: + prefix = '~' + return prefix + self.__name__ +""" +def _typevar__repr__(self): + return "typing." + self.__name__ + +with ensure_import_support(): + # We store all needed modules in signature_loader. + # This way, they are always accessible. + import signature_loader + + if sys.version_info >= (3,): + import typing + import inspect + inspect.formatannotation = formatannotation + else: + import inspect + namespace = inspect.__dict__ + from support.signature import typing27 as typing + typing.__name__ = "typing" + from support.signature import backport_inspect as inspect + _doc = inspect.__doc__ + inspect.__dict__.update(namespace) + inspect.__doc__ += _doc + # force inspect to find all attributes. See "heuristic" in pydoc.py! + inspect.__all__ = list(x for x in dir(inspect) if not x.startswith("_")) + typing.TypeVar.__repr__ = _typevar__repr__ + + def put_into_loader_package(module, loader=signature_loader): + # Note: the "with" statement hides that we are no longer in a + # global context, but inside ensure_import_support. Therefore, + # we need to explicitly pass the signature_loader in. + + # take the last component of the module name + name = module.__name__.rsplit(".", 1)[-1] + # allow access as signature_loader.typing + setattr(loader, name, module) + # put into sys.modules as a package to allow all import options + fullname = "{}.{}".format(loader.__name__, name) + sys.modules[fullname] = module + + put_into_loader_package(typing) + put_into_loader_package(inspect) + from support.signature import mapping as sbk_mapping + sbk_mapping.__name__ = "sbk_mapping" + put_into_loader_package(sbk_mapping) + # We may or may not use PySide. + try: + from PySide2.support.signature import mapping + except ModuleNotFoundError: + mapping = sbk_mapping + mapping.__name__ = "mapping" + put_into_loader_package(mapping) + from support.signature import layout + put_into_loader_package(layout) + from support.signature.lib import enum_sig + put_into_loader_package(enum_sig) + from support.signature.parser import pyside_type_init + + +# Note also that during the tests we have a different encoding that would +# break the Python license decorated files without an encoding line. + +# name used in signature.cpp +def create_signature(props, key): + return layout.create_signature(props, key) + +# name used in signature.cpp +def seterror_argument(args, func_name): + return errorhandler.seterror_argument(args, func_name) + +# end of file diff --git a/sources/shiboken2/shibokenmodule/support/signature/mapping.py b/sources/shiboken2/shibokenmodule/support/signature/mapping.py new file mode 100644 index 000000000..3e76cd94a --- /dev/null +++ b/sources/shiboken2/shibokenmodule/support/signature/mapping.py @@ -0,0 +1,210 @@ +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of Qt for Python. +## +## $QT_BEGIN_LICENSE:LGPL$ +## 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 Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## 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-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +from __future__ import print_function, absolute_import + +""" +mapping.py + +This module has the mapping from the pyside C-modules view of signatures +to the Python representation. + +The PySide modules are not loaded in advance, but only after they appear +in sys.modules. This minimizes the loading overhead. +""" + +import sys +import struct +import os +import pkgutil + +from signature_loader import typing + +class ellipsis(object): + def __repr__(self): + return "..." +ellipsis = ellipsis() +Char = typing.Union[str, int] # how do I model the limitation to 1 char? +StringList = typing.List[str] +IntList = typing.List[int] +Point = typing.Tuple[float, float] +PointList = typing.List[Point] +IntMatrix = typing.List[IntList] +Variant = typing.Any +ModelIndexList = typing.List[int] +QImageCleanupFunction = typing.Callable +FloatList = typing.List[float] +FloatMatrix = typing.List[FloatList] +# Pair could be more specific, but we loose the info in the generator. +Pair = typing.Tuple[typing.Any, typing.Any] +MultiMap = typing.DefaultDict[str, typing.List[str]] + +# ulong_max is only 32 bit on windows. +ulong_max = 2*sys.maxsize+1 if len(struct.pack("L", 1)) != 4 else 0xffffffff +ushort_max = 0xffff + +GL_COLOR_BUFFER_BIT = 0x00004000 +GL_NEAREST = 0x2600 + +WId = int + +# from 5.9 +GL_TEXTURE_2D = 0x0DE1 +GL_RGBA = 0x1908 + +class _NotCalled(str): + """ + Wrap some text with semantics + + This class is wrapped around text in order to avoid calling it. + There are three reasons for this: + + - some instances cannot be created since they are abstract, + - some can only be created after qApp was created, + - some have an ugly __repr__ with angle brackets in it. + + By using derived classes, good looking instances can be created + which can be used to generate source code or .pyi files. When the + real object is needed, the wrapper can simply be called. + """ + def __repr__(self): + suppress = "support.signature.typing." + text = self[len(suppress):] if self.startswith(suppress) else self + return "{}({})".format(type(self).__name__, text) + + def __call__(self): + from signature_loader.mapping import __dict__ as namespace + text = self if self.endswith(")") else self + "()" + return eval(text, namespace) + +# Some types are abstract. They just show their name. +class Virtual(_NotCalled): + pass + +# Other types I simply could not find. +class Missing(_NotCalled): + def __repr__(self): + return '{}("{}")'.format(type(self).__name__, self) + +class Invalid(_NotCalled): + pass + +# Helper types +class Default(_NotCalled): + pass + +class Instance(_NotCalled): + pass + + +class Reloader(object): + _uninitialized = ["sample"] + _prefixes = [""] + + def __init__(self): + self.sys_module_count = 0 + self.uninitialized = self._uninitialized + + def update(self, g=None): + if self.sys_module_count == len(sys.modules): + return + self.sys_module_count = len(sys.modules) + if g is None: + g = globals() + for mod_name in self.uninitialized[:]: + for prefix in self._prefixes: + import_name = prefix + mod_name + if import_name in sys.modules: + # check if this is a real module + obj = sys.modules[import_name] + if not getattr(obj, "__file__", None) or os.path.isdir(obj.__file__): + raise ImportError("Module '{mod_name}' is at most a " + "namespace!".format(**locals())) + # module is real + self.uninitialized.remove(mod_name) + proc_name = "init_" + mod_name + if proc_name in g: + g.update(g[proc_name]()) + + +update_mapping = Reloader().update +type_map = {} + + +def init_sample(): + import sample + import datetime + type_map.update({ + "sample.int": int, + "Complex": complex, + "sample.OddBool": bool, + "sample.bool": bool, + "sample.PStr": str, + "double[]": FloatList, + "OddBool": bool, + "PStr": str, + "sample.char": Char, + "double[][]": FloatMatrix, + "int[]": IntList, + "int[][]": IntMatrix, + "sample.Point": Point, + "sample.ObjectType": object, + "std.string": str, + "HANDLE": int, + "Foo.HANDLE": int, + "sample.Photon.TemplateBase": Missing("sample.Photon.TemplateBase"), + "ObjectType.Identifier": Missing("sample.ObjectType.Identifier"), + "zero(HANDLE)": 0, + "Null": None, + "zero(sample.ObjectType)": None, + "std.size_t": int, + 'Str("<unknown>")': "<unknown>", + 'Str("<unk")': "<unk", + 'Str("nown>")': "nown>", + "zero(sample.ObjectModel)": None, + "sample.unsigned char": Char, + "sample.double": float, + "zero(sample.bool)": False, + "PyDate": datetime.date, + "ZeroIn": 0, + "Point[]": PointList, + }) + return locals() + +# end of file diff --git a/sources/shiboken2/shibokenmodule/support/signature/parser.py b/sources/shiboken2/shibokenmodule/support/signature/parser.py new file mode 100644 index 000000000..5178d9ef9 --- /dev/null +++ b/sources/shiboken2/shibokenmodule/support/signature/parser.py @@ -0,0 +1,286 @@ +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of Qt for Python. +## +## $QT_BEGIN_LICENSE:LGPL$ +## 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 Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## 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-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +from __future__ import print_function, absolute_import + +import sys +import re +import warnings +import types +import keyword +import functools +from signature_loader.mapping import ( + type_map, update_mapping, __dict__ as namespace) + +_DEBUG = False +LIST_KEYWORDS = False + +""" +parser.py + +This module parses the signature text and creates properties for the +signature objects. + +PySide has a new function 'CppGenerator::writeSignatureInfo()' +that extracts the gathered information about the function arguments +and defaults as good as it can. But what PySide generates is still +very C-ish and has many constants that Python doesn't understand. + +The function 'try_to_guess()' below understands a lot of PySide's +peculiar way to assume local context. If it is able to do the guess, +then the result is inserted into the dict, so the search happens +not again. For everything that is not covered by these automatic +guesses, we provide an entry in 'type_map' that resolves it. + +In effect, 'type_map' maps text to real Python objects. +""" + +def dprint(*args, **kw): + if _DEBUG: + import pprint + for arg in args: + pprint.pprint(arg) + sys.stdout.flush() + +def _parse_line(line): + line_re = r""" + ((?P<multi> ([0-9]+)) : )? # the optional multi-index + (?P<funcname> \w+(\.\w+)*) # the function name + \( (?P<arglist> .*?) \) # the argument list + ( -> (?P<returntype> .*) )? # the optional return type + $ + """ + ret = re.match(line_re, line, re.VERBOSE).groupdict() + arglist = ret["arglist"] + # The following is a split re. The string is broken into pieces which are + # between the recognized strings. Because the re has groups, both the + # strings and the delimiters are returned, where the strings are not + # interesting at all: They are just the commata. + # Note that it is necessary to put the characters with special handling in + # the first group (comma, brace, angle bracket). + # Then they are not recognized there, and we can handle them differently + # in the following expressions. + arglist = list(x.strip() for x in re.split(r""" + ( + (?: # inner group is not capturing + [^,()<>] # no commas or braces or angle brackets + | + \( + (?: + [^()]* # or one brace pair + | + \( + [^()]* # or doubls nested pair + \) + )* + \) + | + < # or one angle bracket pair + [^<>]* + > + )+ # longest possible span + ) # this list is interspersed with "," and surrounded by "" + """, arglist, flags=re.VERBOSE) + if x.strip() not in ("", ",")) + args = [] + for arg in arglist: + name, ann = arg.split(":") + if name in keyword.kwlist: + if LIST_KEYWORDS: + print("KEYWORD", ret) + name = name + "_" + if "=" in ann: + ann, default = ann.split("=") + tup = name, ann, default + else: + tup = name, ann + args.append(tup) + ret["arglist"] = args + multi = ret["multi"] + if multi is not None: + ret["multi"] = int(multi) + funcname = ret["funcname"] + parts = funcname.split(".") + if parts[-1] in keyword.kwlist: + ret["funcname"] = funcname + "_" + return ret + +def make_good_value(thing, valtype): + try: + if thing.endswith("()"): + thing = 'Default("{}")'.format(thing[:-2]) + else: + ret = eval(thing, namespace) + if valtype and repr(ret).startswith("<"): + thing = 'Instance("{}")'.format(thing) + return eval(thing, namespace) + except Exception: + pass + +def try_to_guess(thing, valtype): + if "." not in thing and "(" not in thing: + text = "{}.{}".format(valtype, thing) + ret = make_good_value(text, valtype) + if ret is not None: + return ret + typewords = valtype.split(".") + valwords = thing.split(".") + braceless = valwords[0] # Yes, not -1. Relevant is the overlapped word. + if "(" in braceless: + braceless = braceless[:braceless.index("(")] + for idx, w in enumerate(typewords): + if w == braceless: + text = ".".join(typewords[:idx] + valwords) + ret = make_good_value(text, valtype) + if ret is not None: + return ret + return None + +def _resolve_value(thing, valtype, line): + if thing in ("0", "None") and valtype: + thing = "zero({})".format(valtype) + if thing in type_map: + return type_map[thing] + res = make_good_value(thing, valtype) + if res is not None: + type_map[thing] = res + return res + res = try_to_guess(thing, valtype) if valtype else None + if res is not None: + type_map[thing] = res + return res + warnings.warn("""pyside_type_init: + + UNRECOGNIZED: {!r} + OFFENDING LINE: {!r} + """.format(thing, line), RuntimeWarning) + return thing + +def _resolve_type(thing, line): + return _resolve_value(thing, None, line) + +def calculate_props(line): + line = line.strip() + res = _parse_line(line) + arglist = res["arglist"] + annotations = {} + _defaults = [] + for idx, tup in enumerate(arglist): + name, ann = tup[:2] + if ann == "...": + name = "*args" + # copy the fields back :() + ann = 'NULL' # maps to None + tup = name, ann + arglist[idx] = tup + annotations[name] = _resolve_type(ann, line) + if len(tup) == 3: + default = _resolve_value(tup[2], ann, line) + _defaults.append(default) + defaults = tuple(_defaults) + returntype = res["returntype"] + if returntype is not None: + annotations["return"] = _resolve_type(returntype, line) + props = {} + props["defaults"] = defaults + props["kwdefaults"] = {} + props["annotations"] = annotations + props["varnames"] = varnames = tuple(tup[0] for tup in arglist) + funcname = res["funcname"] + props["fullname"] = funcname + shortname = funcname[funcname.rindex(".")+1:] + props["name"] = shortname + props["multi"] = res["multi"] + return props + +def fixup_multilines(sig_str): + lines = list(line.strip() for line in sig_str.strip().splitlines()) + res = [] + multi_lines = [] + for line in lines: + multi = re.match(r"([0-9]+):", line) + if multi: + idx, rest = int(multi.group(1)), line[multi.end():] + multi_lines.append(rest) + if idx > 0: + continue + # remove duplicates + multi_lines = sorted(set(multi_lines)) + # renumber or return a single line + nmulti = len(multi_lines) + if nmulti > 1: + for idx, line in enumerate(multi_lines): + res.append("{}:{}".format(nmulti-idx-1, line)) + else: + res.append(multi_lines[0]) + multi_lines = [] + else: + res.append(line) + return res + +def pyside_type_init(typemod, sig_str): + dprint() + if type(typemod) is types.ModuleType: + dprint("Initialization of module '{}'".format(typemod.__name__)) + else: + dprint("Initialization of type '{}.{}'".format(typemod.__module__, + typemod.__name__)) + update_mapping() + lines = fixup_multilines(sig_str) + ret = {} + multi_props = [] + for line in lines: + props = calculate_props(line) + shortname = props["name"] + multi = props["multi"] + if multi is None: + ret[shortname] = props + dprint(props) + else: + multi_props.append(props) + if multi > 0: + continue + fullname = props.pop("fullname") + multi_props = {"multi": multi_props, "fullname": fullname} + ret[shortname] = multi_props + dprint(multi_props) + multi_props = [] + return ret + +# end of file diff --git a/sources/shiboken2/shibokenmodule/support/signature/qt_attribution.json b/sources/shiboken2/shibokenmodule/support/signature/qt_attribution.json new file mode 100644 index 000000000..491ae8054 --- /dev/null +++ b/sources/shiboken2/shibokenmodule/support/signature/qt_attribution.json @@ -0,0 +1,13 @@ +{ + "Id": "python", + "Name": "Python", + "QDocModule": "QtForPython", + "QtUsage": "Used for Qt for Python in the signature extension.", + "Description": "Qt for Python is an add-on for Python. The signature packages of PySide uses certain copied and adapted source files (backport_inspect.py, typing27.py). See the folder sources/pyside2/PySide2/support/signature .", + "Homepage": "http://www.python.org/", + "Version": "3.7.0", + "LicenseId": "Python-2.0", + "License": "Python License 2.0", + "LicenseFile": "PSF-3.7.0.txt", + "Copyright": "© Copyright 2001-2018, Python Software Foundation." +} diff --git a/sources/shiboken2/shibokenmodule/support/signature/typing27.py b/sources/shiboken2/shibokenmodule/support/signature/typing27.py new file mode 100644 index 000000000..5d1c6058b --- /dev/null +++ b/sources/shiboken2/shibokenmodule/support/signature/typing27.py @@ -0,0 +1,2293 @@ +# This Python file uses the following encoding: utf-8 +# It has been edited by fix-complaints.py . + +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of Qt for Python. +## +## $QT_BEGIN_LICENSE:LGPL$ +## 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 Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## 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-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +""" +PSF LICENSE AGREEMENT FOR PYTHON 3.7.0 + +1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and + the Individual or Organization ("Licensee") accessing and otherwise using Python + 3.7.0 software in source or binary form and its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF hereby + grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, + analyze, test, perform and/or display publicly, prepare derivative works, + distribute, and otherwise use Python 3.7.0 alone or in any derivative + version, provided, however, that PSF's License Agreement and PSF's notice of + copyright, i.e., "Copyright © 2001-2018 Python Software Foundation; All Rights + Reserved" are retained in Python 3.7.0 alone or in any derivative version + prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on or + incorporates Python 3.7.0 or any part thereof, and wants to make the + derivative work available to others as provided herein, then Licensee hereby + agrees to include in any such work a brief summary of the changes made to Python + 3.7.0. + +4. PSF is making Python 3.7.0 available to Licensee on an "AS IS" basis. + PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF + EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR + WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE + USE OF PYTHON 3.7.0 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 3.7.0 + FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF + MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 3.7.0, OR ANY DERIVATIVE + THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material breach of + its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any relationship + of agency, partnership, or joint venture between PSF and Licensee. This License + Agreement does not grant permission to use PSF trademarks or trade name in a + trademark sense to endorse or promote products or services of Licensee, or any + third party. + +8. By copying, installing or otherwise using Python 3.7.0, Licensee agrees + to be bound by the terms and conditions of this License Agreement. +""" + +from __future__ import absolute_import, unicode_literals + +import abc +from abc import abstractmethod, abstractproperty +import collections +import functools +import re as stdlib_re # Avoid confusion with the re we export. +import sys +import types +import copy +try: + import collections.abc as collections_abc +except ImportError: + import collections as collections_abc # Fallback for PY3.2. + + +# Please keep __all__ alphabetized within each category. +__all__ = [ + # Super-special typing primitives. + 'Any', + 'Callable', + 'ClassVar', + 'Generic', + 'Optional', + 'Tuple', + 'Type', + 'TypeVar', + 'Union', + + # ABCs (from collections.abc). + 'AbstractSet', # collections.abc.Set. + 'GenericMeta', # subclass of abc.ABCMeta and a metaclass + # for 'Generic' and ABCs below. + 'ByteString', + 'Container', + 'ContextManager', + 'Hashable', + 'ItemsView', + 'Iterable', + 'Iterator', + 'KeysView', + 'Mapping', + 'MappingView', + 'MutableMapping', + 'MutableSequence', + 'MutableSet', + 'Sequence', + 'Sized', + 'ValuesView', + + # Structural checks, a.k.a. protocols. + 'Reversible', + 'SupportsAbs', + 'SupportsComplex', + 'SupportsFloat', + 'SupportsInt', + + # Concrete collection types. + 'Counter', + 'Deque', + 'Dict', + 'DefaultDict', + 'List', + 'Set', + 'FrozenSet', + 'NamedTuple', # Not really a type. + 'Generator', + + # One-off things. + 'AnyStr', + 'cast', + 'get_type_hints', + 'NewType', + 'no_type_check', + 'no_type_check_decorator', + 'NoReturn', + 'overload', + 'Text', + 'TYPE_CHECKING', +] + +# The pseudo-submodules 're' and 'io' are part of the public +# namespace, but excluded from __all__ because they might stomp on +# legitimate imports of those modules. + + +def _qualname(x): + if sys.version_info[:2] >= (3, 3): + return x.__qualname__ + else: + # Fall back to just name. + return x.__name__ + + +def _trim_name(nm): + whitelist = ('_TypeAlias', '_ForwardRef', '_TypingBase', '_FinalTypingBase') + if nm.startswith('_') and nm not in whitelist: + nm = nm[1:] + return nm + + +class TypingMeta(type): + """Metaclass for most types defined in typing module + (not a part of public API). + + This also defines a dummy constructor (all the work for most typing + constructs is done in __new__) and a nicer repr(). + """ + + _is_protocol = False + + def __new__(cls, name, bases, namespace): + return super(TypingMeta, cls).__new__(cls, str(name), bases, namespace) + + @classmethod + def assert_no_subclassing(cls, bases): + for base in bases: + if isinstance(base, cls): + raise TypeError("Cannot subclass %s" % + (', '.join(map(_type_repr, bases)) or '()')) + + def __init__(self, *args, **kwds): + pass + + def _eval_type(self, globalns, localns): + """Override this in subclasses to interpret forward references. + + For example, List['C'] is internally stored as + List[_ForwardRef('C')], which should evaluate to List[C], + where C is an object found in globalns or localns (searching + localns first, of course). + """ + return self + + def _get_type_vars(self, tvars): + pass + + def __repr__(self): + qname = _trim_name(_qualname(self)) + return '%s.%s' % (self.__module__, qname) + + +class _TypingBase(object): + """Internal indicator of special typing constructs.""" + __metaclass__ = TypingMeta + __slots__ = ('__weakref__',) + + def __init__(self, *args, **kwds): + pass + + def __new__(cls, *args, **kwds): + """Constructor. + + This only exists to give a better error message in case + someone tries to subclass a special typing object (not a good idea). + """ + if (len(args) == 3 and + isinstance(args[0], str) and + isinstance(args[1], tuple)): + # Close enough. + raise TypeError("Cannot subclass %r" % cls) + return super(_TypingBase, cls).__new__(cls) + + # Things that are not classes also need these. + def _eval_type(self, globalns, localns): + return self + + def _get_type_vars(self, tvars): + pass + + def __repr__(self): + cls = type(self) + qname = _trim_name(_qualname(cls)) + return '%s.%s' % (cls.__module__, qname) + + def __call__(self, *args, **kwds): + raise TypeError("Cannot instantiate %r" % type(self)) + + +class _FinalTypingBase(_TypingBase): + """Internal mix-in class to prevent instantiation. + + Prevents instantiation unless _root=True is given in class call. + It is used to create pseudo-singleton instances Any, Union, Optional, etc. + """ + + __slots__ = () + + def __new__(cls, *args, **kwds): + self = super(_FinalTypingBase, cls).__new__(cls, *args, **kwds) + if '_root' in kwds and kwds['_root'] is True: + return self + raise TypeError("Cannot instantiate %r" % cls) + + def __reduce__(self): + return _trim_name(type(self).__name__) + + +class _ForwardRef(_TypingBase): + """Internal wrapper to hold a forward reference.""" + + __slots__ = ('__forward_arg__', '__forward_code__', + '__forward_evaluated__', '__forward_value__') + + def __init__(self, arg): + super(_ForwardRef, self).__init__(arg) + if not isinstance(arg, basestring): + raise TypeError('Forward reference must be a string -- got %r' % (arg,)) + try: + code = compile(arg, '<string>', 'eval') + except SyntaxError: + raise SyntaxError('Forward reference must be an expression -- got %r' % + (arg,)) + self.__forward_arg__ = arg + self.__forward_code__ = code + self.__forward_evaluated__ = False + self.__forward_value__ = None + + def _eval_type(self, globalns, localns): + if not self.__forward_evaluated__ or localns is not globalns: + if globalns is None and localns is None: + globalns = localns = {} + elif globalns is None: + globalns = localns + elif localns is None: + localns = globalns + self.__forward_value__ = _type_check( + eval(self.__forward_code__, globalns, localns), + "Forward references must evaluate to types.") + self.__forward_evaluated__ = True + return self.__forward_value__ + + def __eq__(self, other): + if not isinstance(other, _ForwardRef): + return NotImplemented + return (self.__forward_arg__ == other.__forward_arg__ and + self.__forward_value__ == other.__forward_value__) + + def __hash__(self): + return hash((self.__forward_arg__, self.__forward_value__)) + + def __instancecheck__(self, obj): + raise TypeError("Forward references cannot be used with isinstance().") + + def __subclasscheck__(self, cls): + raise TypeError("Forward references cannot be used with issubclass().") + + def __repr__(self): + return '_ForwardRef(%r)' % (self.__forward_arg__,) + + +class _TypeAlias(_TypingBase): + """Internal helper class for defining generic variants of concrete types. + + Note that this is not a type; let's call it a pseudo-type. It cannot + be used in instance and subclass checks in parameterized form, i.e. + ``isinstance(42, Match[str])`` raises ``TypeError`` instead of returning + ``False``. + """ + + __slots__ = ('name', 'type_var', 'impl_type', 'type_checker') + + def __init__(self, name, type_var, impl_type, type_checker): + """Initializer. + + Args: + name: The name, e.g. 'Pattern'. + type_var: The type parameter, e.g. AnyStr, or the + specific type, e.g. str. + impl_type: The implementation type. + type_checker: Function that takes an impl_type instance. + and returns a value that should be a type_var instance. + """ + assert isinstance(name, basestring), repr(name) + assert isinstance(impl_type, type), repr(impl_type) + assert not isinstance(impl_type, TypingMeta), repr(impl_type) + assert isinstance(type_var, (type, _TypingBase)), repr(type_var) + self.name = name + self.type_var = type_var + self.impl_type = impl_type + self.type_checker = type_checker + + def __repr__(self): + return "%s[%s]" % (self.name, _type_repr(self.type_var)) + + def __getitem__(self, parameter): + if not isinstance(self.type_var, TypeVar): + raise TypeError("%s cannot be further parameterized." % self) + if self.type_var.__constraints__ and isinstance(parameter, type): + if not issubclass(parameter, self.type_var.__constraints__): + raise TypeError("%s is not a valid substitution for %s." % + (parameter, self.type_var)) + if isinstance(parameter, TypeVar) and parameter is not self.type_var: + raise TypeError("%s cannot be re-parameterized." % self) + return self.__class__(self.name, parameter, + self.impl_type, self.type_checker) + + def __eq__(self, other): + if not isinstance(other, _TypeAlias): + return NotImplemented + return self.name == other.name and self.type_var == other.type_var + + def __hash__(self): + return hash((self.name, self.type_var)) + + def __instancecheck__(self, obj): + if not isinstance(self.type_var, TypeVar): + raise TypeError("Parameterized type aliases cannot be used " + "with isinstance().") + return isinstance(obj, self.impl_type) + + def __subclasscheck__(self, cls): + if not isinstance(self.type_var, TypeVar): + raise TypeError("Parameterized type aliases cannot be used " + "with issubclass().") + return issubclass(cls, self.impl_type) + + +def _get_type_vars(types, tvars): + for t in types: + if isinstance(t, TypingMeta) or isinstance(t, _TypingBase): + t._get_type_vars(tvars) + + +def _type_vars(types): + tvars = [] + _get_type_vars(types, tvars) + return tuple(tvars) + + +def _eval_type(t, globalns, localns): + if isinstance(t, TypingMeta) or isinstance(t, _TypingBase): + return t._eval_type(globalns, localns) + return t + + +def _type_check(arg, msg): + """Check that the argument is a type, and return it (internal helper). + + As a special case, accept None and return type(None) instead. + Also, _TypeAlias instances (e.g. Match, Pattern) are acceptable. + + The msg argument is a human-readable error message, e.g. + + "Union[arg, ...]: arg should be a type." + + We append the repr() of the actual value (truncated to 100 chars). + """ + if arg is None: + return type(None) + if isinstance(arg, basestring): + arg = _ForwardRef(arg) + if ( + isinstance(arg, _TypingBase) and type(arg).__name__ == '_ClassVar' or + not isinstance(arg, (type, _TypingBase)) and not callable(arg) + ): + raise TypeError(msg + " Got %.100r." % (arg,)) + # Bare Union etc. are not valid as type arguments + if ( + type(arg).__name__ in ('_Union', '_Optional') and + not getattr(arg, '__origin__', None) or + isinstance(arg, TypingMeta) and arg._gorg in (Generic, _Protocol) + ): + raise TypeError("Plain %s is not valid as type argument" % arg) + return arg + + +def _type_repr(obj): + """Return the repr() of an object, special-casing types (internal helper). + + If obj is a type, we return a shorter version than the default + type.__repr__, based on the module and qualified name, which is + typically enough to uniquely identify a type. For everything + else, we fall back on repr(obj). + """ + if isinstance(obj, type) and not isinstance(obj, TypingMeta): + if obj.__module__ == '__builtin__': + return _qualname(obj) + return '%s.%s' % (obj.__module__, _qualname(obj)) + if obj is Ellipsis: + return('...') + if isinstance(obj, types.FunctionType): + return obj.__name__ + return repr(obj) + + +class ClassVarMeta(TypingMeta): + """Metaclass for _ClassVar""" + + def __new__(cls, name, bases, namespace): + cls.assert_no_subclassing(bases) + self = super(ClassVarMeta, cls).__new__(cls, name, bases, namespace) + return self + + +class _ClassVar(_FinalTypingBase): + """Special type construct to mark class variables. + + An annotation wrapped in ClassVar indicates that a given + attribute is intended to be used as a class variable and + should not be set on instances of that class. Usage:: + + class Starship: + stats = {} # type: ClassVar[Dict[str, int]] # class variable + damage = 10 # type: int # instance variable + + ClassVar accepts only types and cannot be further subscribed. + + Note that ClassVar is not a class itself, and should not + be used with isinstance() or issubclass(). + """ + + __metaclass__ = ClassVarMeta + __slots__ = ('__type__',) + + def __init__(self, tp=None, _root=False): + self.__type__ = tp + + def __getitem__(self, item): + cls = type(self) + if self.__type__ is None: + return cls(_type_check(item, + '{} accepts only types.'.format(cls.__name__[1:])), + _root=True) + raise TypeError('{} cannot be further subscripted' + .format(cls.__name__[1:])) + + def _eval_type(self, globalns, localns): + return type(self)(_eval_type(self.__type__, globalns, localns), + _root=True) + + def __repr__(self): + r = super(_ClassVar, self).__repr__() + if self.__type__ is not None: + r += '[{}]'.format(_type_repr(self.__type__)) + return r + + def __hash__(self): + return hash((type(self).__name__, self.__type__)) + + def __eq__(self, other): + if not isinstance(other, _ClassVar): + return NotImplemented + if self.__type__ is not None: + return self.__type__ == other.__type__ + return self is other + + +ClassVar = _ClassVar(_root=True) + + +class AnyMeta(TypingMeta): + """Metaclass for Any.""" + + def __new__(cls, name, bases, namespace): + cls.assert_no_subclassing(bases) + self = super(AnyMeta, cls).__new__(cls, name, bases, namespace) + return self + + +class _Any(_FinalTypingBase): + """Special type indicating an unconstrained type. + + - Any is compatible with every type. + - Any assumed to have all methods. + - All values assumed to be instances of Any. + + Note that all the above statements are true from the point of view of + static type checkers. At runtime, Any should not be used with instance + or class checks. + """ + __metaclass__ = AnyMeta + __slots__ = () + + def __instancecheck__(self, obj): + raise TypeError("Any cannot be used with isinstance().") + + def __subclasscheck__(self, cls): + raise TypeError("Any cannot be used with issubclass().") + + +Any = _Any(_root=True) + + +class NoReturnMeta(TypingMeta): + """Metaclass for NoReturn.""" + + def __new__(cls, name, bases, namespace): + cls.assert_no_subclassing(bases) + self = super(NoReturnMeta, cls).__new__(cls, name, bases, namespace) + return self + + +class _NoReturn(_FinalTypingBase): + """Special type indicating functions that never return. + Example:: + + from typing import NoReturn + + def stop() -> NoReturn: + raise Exception('no way') + + This type is invalid in other positions, e.g., ``List[NoReturn]`` + will fail in static type checkers. + """ + __metaclass__ = NoReturnMeta + __slots__ = () + + def __instancecheck__(self, obj): + raise TypeError("NoReturn cannot be used with isinstance().") + + def __subclasscheck__(self, cls): + raise TypeError("NoReturn cannot be used with issubclass().") + + +NoReturn = _NoReturn(_root=True) + + +class TypeVarMeta(TypingMeta): + def __new__(cls, name, bases, namespace): + cls.assert_no_subclassing(bases) + return super(TypeVarMeta, cls).__new__(cls, name, bases, namespace) + + +class TypeVar(_TypingBase): + """Type variable. + + Usage:: + + T = TypeVar('T') # Can be anything + A = TypeVar('A', str, bytes) # Must be str or bytes + + Type variables exist primarily for the benefit of static type + checkers. They serve as the parameters for generic types as well + as for generic function definitions. See class Generic for more + information on generic types. Generic functions work as follows: + + def repeat(x: T, n: int) -> List[T]: + '''Return a list containing n references to x.''' + return [x]*n + + def longest(x: A, y: A) -> A: + '''Return the longest of two strings.''' + return x if len(x) >= len(y) else y + + The latter example's signature is essentially the overloading + of (str, str) -> str and (bytes, bytes) -> bytes. Also note + that if the arguments are instances of some subclass of str, + the return type is still plain str. + + At runtime, isinstance(x, T) and issubclass(C, T) will raise TypeError. + + Type variables defined with covariant=True or contravariant=True + can be used do declare covariant or contravariant generic types. + See PEP 484 for more details. By default generic types are invariant + in all type variables. + + Type variables can be introspected. e.g.: + + T.__name__ == 'T' + T.__constraints__ == () + T.__covariant__ == False + T.__contravariant__ = False + A.__constraints__ == (str, bytes) + """ + + __metaclass__ = TypeVarMeta + __slots__ = ('__name__', '__bound__', '__constraints__', + '__covariant__', '__contravariant__') + + def __init__(self, name, *constraints, **kwargs): + super(TypeVar, self).__init__(name, *constraints, **kwargs) + bound = kwargs.get('bound', None) + covariant = kwargs.get('covariant', False) + contravariant = kwargs.get('contravariant', False) + self.__name__ = name + if covariant and contravariant: + raise ValueError("Bivariant types are not supported.") + self.__covariant__ = bool(covariant) + self.__contravariant__ = bool(contravariant) + if constraints and bound is not None: + raise TypeError("Constraints cannot be combined with bound=...") + if constraints and len(constraints) == 1: + raise TypeError("A single constraint is not allowed") + msg = "TypeVar(name, constraint, ...): constraints must be types." + self.__constraints__ = tuple(_type_check(t, msg) for t in constraints) + if bound: + self.__bound__ = _type_check(bound, "Bound must be a type.") + else: + self.__bound__ = None + + def _get_type_vars(self, tvars): + if self not in tvars: + tvars.append(self) + + def __repr__(self): + if self.__covariant__: + prefix = '+' + elif self.__contravariant__: + prefix = '-' + else: + prefix = '~' + return prefix + self.__name__ + + def __instancecheck__(self, instance): + raise TypeError("Type variables cannot be used with isinstance().") + + def __subclasscheck__(self, cls): + raise TypeError("Type variables cannot be used with issubclass().") + + +# Some unconstrained type variables. These are used by the container types. +# (These are not for export.) +T = TypeVar('T') # Any type. +KT = TypeVar('KT') # Key type. +VT = TypeVar('VT') # Value type. +T_co = TypeVar('T_co', covariant=True) # Any type covariant containers. +V_co = TypeVar('V_co', covariant=True) # Any type covariant containers. +VT_co = TypeVar('VT_co', covariant=True) # Value type covariant containers. +T_contra = TypeVar('T_contra', contravariant=True) # Ditto contravariant. + +# A useful type variable with constraints. This represents string types. +# (This one *is* for export!) +AnyStr = TypeVar('AnyStr', bytes, unicode) + + +def _replace_arg(arg, tvars, args): + """An internal helper function: replace arg if it is a type variable + found in tvars with corresponding substitution from args or + with corresponding substitution sub-tree if arg is a generic type. + """ + + if tvars is None: + tvars = [] + if hasattr(arg, '_subs_tree') and isinstance(arg, (GenericMeta, _TypingBase)): + return arg._subs_tree(tvars, args) + if isinstance(arg, TypeVar): + for i, tvar in enumerate(tvars): + if arg == tvar: + return args[i] + return arg + + +# Special typing constructs Union, Optional, Generic, Callable and Tuple +# use three special attributes for internal bookkeeping of generic types: +# * __parameters__ is a tuple of unique free type parameters of a generic +# type, for example, Dict[T, T].__parameters__ == (T,); +# * __origin__ keeps a reference to a type that was subscripted, +# e.g., Union[T, int].__origin__ == Union; +# * __args__ is a tuple of all arguments used in subscripting, +# e.g., Dict[T, int].__args__ == (T, int). + + +def _subs_tree(cls, tvars=None, args=None): + """An internal helper function: calculate substitution tree + for generic cls after replacing its type parameters with + substitutions in tvars -> args (if any). + Repeat the same following __origin__'s. + + Return a list of arguments with all possible substitutions + performed. Arguments that are generic classes themselves are represented + as tuples (so that no new classes are created by this function). + For example: _subs_tree(List[Tuple[int, T]][str]) == [(Tuple, int, str)] + """ + + if cls.__origin__ is None: + return cls + # Make of chain of origins (i.e. cls -> cls.__origin__) + current = cls.__origin__ + orig_chain = [] + while current.__origin__ is not None: + orig_chain.append(current) + current = current.__origin__ + # Replace type variables in __args__ if asked ... + tree_args = [] + for arg in cls.__args__: + tree_args.append(_replace_arg(arg, tvars, args)) + # ... then continue replacing down the origin chain. + for ocls in orig_chain: + new_tree_args = [] + for arg in ocls.__args__: + new_tree_args.append(_replace_arg(arg, ocls.__parameters__, tree_args)) + tree_args = new_tree_args + return tree_args + + +def _remove_dups_flatten(parameters): + """An internal helper for Union creation and substitution: flatten Union's + among parameters, then remove duplicates and strict subclasses. + """ + + # Flatten out Union[Union[...], ...]. + params = [] + for p in parameters: + if isinstance(p, _Union) and p.__origin__ is Union: + params.extend(p.__args__) + elif isinstance(p, tuple) and len(p) > 0 and p[0] is Union: + params.extend(p[1:]) + else: + params.append(p) + # Weed out strict duplicates, preserving the first of each occurrence. + all_params = set(params) + if len(all_params) < len(params): + new_params = [] + for t in params: + if t in all_params: + new_params.append(t) + all_params.remove(t) + params = new_params + assert not all_params, all_params + # Weed out subclasses. + # E.g. Union[int, Employee, Manager] == Union[int, Employee]. + # If object is present it will be sole survivor among proper classes. + # Never discard type variables. + # (In particular, Union[str, AnyStr] != AnyStr.) + all_params = set(params) + for t1 in params: + if not isinstance(t1, type): + continue + if any(isinstance(t2, type) and issubclass(t1, t2) + for t2 in all_params - {t1} + if not (isinstance(t2, GenericMeta) and + t2.__origin__ is not None)): + all_params.remove(t1) + return tuple(t for t in params if t in all_params) + + +def _check_generic(cls, parameters): + # Check correct count for parameters of a generic cls (internal helper). + if not cls.__parameters__: + raise TypeError("%s is not a generic class" % repr(cls)) + alen = len(parameters) + elen = len(cls.__parameters__) + if alen != elen: + raise TypeError("Too %s parameters for %s; actual %s, expected %s" % + ("many" if alen > elen else "few", repr(cls), alen, elen)) + + +_cleanups = [] + + +def _tp_cache(func): + maxsize = 128 + cache = {} + _cleanups.append(cache.clear) + + @functools.wraps(func) + def inner(*args): + key = args + try: + return cache[key] + except TypeError: + # Assume it's an unhashable argument. + return func(*args) + except KeyError: + value = func(*args) + if len(cache) >= maxsize: + # If the cache grows too much, just start over. + cache.clear() + cache[key] = value + return value + + return inner + + +class UnionMeta(TypingMeta): + """Metaclass for Union.""" + + def __new__(cls, name, bases, namespace): + cls.assert_no_subclassing(bases) + return super(UnionMeta, cls).__new__(cls, name, bases, namespace) + + +class _Union(_FinalTypingBase): + """Union type; Union[X, Y] means either X or Y. + + To define a union, use e.g. Union[int, str]. Details: + + - The arguments must be types and there must be at least one. + + - None as an argument is a special case and is replaced by + type(None). + + - Unions of unions are flattened, e.g.:: + + Union[Union[int, str], float] == Union[int, str, float] + + - Unions of a single argument vanish, e.g.:: + + Union[int] == int # The constructor actually returns int + + - Redundant arguments are skipped, e.g.:: + + Union[int, str, int] == Union[int, str] + + - When comparing unions, the argument order is ignored, e.g.:: + + Union[int, str] == Union[str, int] + + - When two arguments have a subclass relationship, the least + derived argument is kept, e.g.:: + + class Employee: pass + class Manager(Employee): pass + Union[int, Employee, Manager] == Union[int, Employee] + Union[Manager, int, Employee] == Union[int, Employee] + Union[Employee, Manager] == Employee + + - Similar for object:: + + Union[int, object] == object + + - You cannot subclass or instantiate a union. + + - You can use Optional[X] as a shorthand for Union[X, None]. + """ + + __metaclass__ = UnionMeta + __slots__ = ('__parameters__', '__args__', '__origin__', '__tree_hash__') + + def __new__(cls, parameters=None, origin=None, *args, **kwds): + self = super(_Union, cls).__new__(cls, parameters, origin, *args, **kwds) + if origin is None: + self.__parameters__ = None + self.__args__ = None + self.__origin__ = None + self.__tree_hash__ = hash(frozenset(('Union',))) + return self + if not isinstance(parameters, tuple): + raise TypeError("Expected parameters=<tuple>") + if origin is Union: + parameters = _remove_dups_flatten(parameters) + # It's not a union if there's only one type left. + if len(parameters) == 1: + return parameters[0] + self.__parameters__ = _type_vars(parameters) + self.__args__ = parameters + self.__origin__ = origin + # Pre-calculate the __hash__ on instantiation. + # This improves speed for complex substitutions. + subs_tree = self._subs_tree() + if isinstance(subs_tree, tuple): + self.__tree_hash__ = hash(frozenset(subs_tree)) + else: + self.__tree_hash__ = hash(subs_tree) + return self + + def _eval_type(self, globalns, localns): + if self.__args__ is None: + return self + ev_args = tuple(_eval_type(t, globalns, localns) for t in self.__args__) + ev_origin = _eval_type(self.__origin__, globalns, localns) + if ev_args == self.__args__ and ev_origin == self.__origin__: + # Everything is already evaluated. + return self + return self.__class__(ev_args, ev_origin, _root=True) + + def _get_type_vars(self, tvars): + if self.__origin__ and self.__parameters__: + _get_type_vars(self.__parameters__, tvars) + + def __repr__(self): + if self.__origin__ is None: + return super(_Union, self).__repr__() + tree = self._subs_tree() + if not isinstance(tree, tuple): + return repr(tree) + return tree[0]._tree_repr(tree) + + def _tree_repr(self, tree): + arg_list = [] + for arg in tree[1:]: + if not isinstance(arg, tuple): + arg_list.append(_type_repr(arg)) + else: + arg_list.append(arg[0]._tree_repr(arg)) + return super(_Union, self).__repr__() + '[%s]' % ', '.join(arg_list) + + @_tp_cache + def __getitem__(self, parameters): + if parameters == (): + raise TypeError("Cannot take a Union of no types.") + if not isinstance(parameters, tuple): + parameters = (parameters,) + if self.__origin__ is None: + msg = "Union[arg, ...]: each arg must be a type." + else: + msg = "Parameters to generic types must be types." + parameters = tuple(_type_check(p, msg) for p in parameters) + if self is not Union: + _check_generic(self, parameters) + return self.__class__(parameters, origin=self, _root=True) + + def _subs_tree(self, tvars=None, args=None): + if self is Union: + return Union # Nothing to substitute + tree_args = _subs_tree(self, tvars, args) + tree_args = _remove_dups_flatten(tree_args) + if len(tree_args) == 1: + return tree_args[0] # Union of a single type is that type + return (Union,) + tree_args + + def __eq__(self, other): + if isinstance(other, _Union): + return self.__tree_hash__ == other.__tree_hash__ + elif self is not Union: + return self._subs_tree() == other + else: + return self is other + + def __hash__(self): + return self.__tree_hash__ + + def __instancecheck__(self, obj): + raise TypeError("Unions cannot be used with isinstance().") + + def __subclasscheck__(self, cls): + raise TypeError("Unions cannot be used with issubclass().") + + +Union = _Union(_root=True) + + +class OptionalMeta(TypingMeta): + """Metaclass for Optional.""" + + def __new__(cls, name, bases, namespace): + cls.assert_no_subclassing(bases) + return super(OptionalMeta, cls).__new__(cls, name, bases, namespace) + + +class _Optional(_FinalTypingBase): + """Optional type. + + Optional[X] is equivalent to Union[X, None]. + """ + + __metaclass__ = OptionalMeta + __slots__ = () + + @_tp_cache + def __getitem__(self, arg): + arg = _type_check(arg, "Optional[t] requires a single type.") + return Union[arg, type(None)] + + +Optional = _Optional(_root=True) + + +def _next_in_mro(cls): + """Helper for Generic.__new__. + + Returns the class after the last occurrence of Generic or + Generic[...] in cls.__mro__. + """ + next_in_mro = object + # Look for the last occurrence of Generic or Generic[...]. + for i, c in enumerate(cls.__mro__[:-1]): + if isinstance(c, GenericMeta) and c._gorg is Generic: + next_in_mro = cls.__mro__[i + 1] + return next_in_mro + + +def _make_subclasshook(cls): + """Construct a __subclasshook__ callable that incorporates + the associated __extra__ class in subclass checks performed + against cls. + """ + if isinstance(cls.__extra__, abc.ABCMeta): + # The logic mirrors that of ABCMeta.__subclasscheck__. + # Registered classes need not be checked here because + # cls and its extra share the same _abc_registry. + def __extrahook__(cls, subclass): + res = cls.__extra__.__subclasshook__(subclass) + if res is not NotImplemented: + return res + if cls.__extra__ in getattr(subclass, '__mro__', ()): + return True + for scls in cls.__extra__.__subclasses__(): + if isinstance(scls, GenericMeta): + continue + if issubclass(subclass, scls): + return True + return NotImplemented + else: + # For non-ABC extras we'll just call issubclass(). + def __extrahook__(cls, subclass): + if cls.__extra__ and issubclass(subclass, cls.__extra__): + return True + return NotImplemented + return classmethod(__extrahook__) + + +class GenericMeta(TypingMeta, abc.ABCMeta): + """Metaclass for generic types. + + This is a metaclass for typing.Generic and generic ABCs defined in + typing module. User defined subclasses of GenericMeta can override + __new__ and invoke super().__new__. Note that GenericMeta.__new__ + has strict rules on what is allowed in its bases argument: + * plain Generic is disallowed in bases; + * Generic[...] should appear in bases at most once; + * if Generic[...] is present, then it should list all type variables + that appear in other bases. + In addition, type of all generic bases is erased, e.g., C[int] is + stripped to plain C. + """ + + def __new__(cls, name, bases, namespace, + tvars=None, args=None, origin=None, extra=None, orig_bases=None): + """Create a new generic class. GenericMeta.__new__ accepts + keyword arguments that are used for internal bookkeeping, therefore + an override should pass unused keyword arguments to super(). + """ + if tvars is not None: + # Called from __getitem__() below. + assert origin is not None + assert all(isinstance(t, TypeVar) for t in tvars), tvars + else: + # Called from class statement. + assert tvars is None, tvars + assert args is None, args + assert origin is None, origin + + # Get the full set of tvars from the bases. + tvars = _type_vars(bases) + # Look for Generic[T1, ..., Tn]. + # If found, tvars must be a subset of it. + # If not found, tvars is it. + # Also check for and reject plain Generic, + # and reject multiple Generic[...]. + gvars = None + for base in bases: + if base is Generic: + raise TypeError("Cannot inherit from plain Generic") + if (isinstance(base, GenericMeta) and + base.__origin__ is Generic): + if gvars is not None: + raise TypeError( + "Cannot inherit from Generic[...] multiple types.") + gvars = base.__parameters__ + if gvars is None: + gvars = tvars + else: + tvarset = set(tvars) + gvarset = set(gvars) + if not tvarset <= gvarset: + raise TypeError( + "Some type variables (%s) " + "are not listed in Generic[%s]" % + (", ".join(str(t) for t in tvars if t not in gvarset), + ", ".join(str(g) for g in gvars))) + tvars = gvars + + initial_bases = bases + if extra is None: + extra = namespace.get('__extra__') + if extra is not None and type(extra) is abc.ABCMeta and extra not in bases: + bases = (extra,) + bases + bases = tuple(b._gorg if isinstance(b, GenericMeta) else b for b in bases) + + # remove bare Generic from bases if there are other generic bases + if any(isinstance(b, GenericMeta) and b is not Generic for b in bases): + bases = tuple(b for b in bases if b is not Generic) + namespace.update({'__origin__': origin, '__extra__': extra}) + self = super(GenericMeta, cls).__new__(cls, name, bases, namespace) + super(GenericMeta, self).__setattr__('_gorg', + self if not origin else origin._gorg) + + self.__parameters__ = tvars + # Be prepared that GenericMeta will be subclassed by TupleMeta + # and CallableMeta, those two allow ..., (), or [] in __args___. + self.__args__ = tuple(Ellipsis if a is _TypingEllipsis else + () if a is _TypingEmpty else + a for a in args) if args else None + # Speed hack (https://github.com/python/typing/issues/196). + self.__next_in_mro__ = _next_in_mro(self) + # Preserve base classes on subclassing (__bases__ are type erased now). + if orig_bases is None: + self.__orig_bases__ = initial_bases + + # This allows unparameterized generic collections to be used + # with issubclass() and isinstance() in the same way as their + # collections.abc counterparts (e.g., isinstance([], Iterable)). + if ( + '__subclasshook__' not in namespace and extra or + # allow overriding + getattr(self.__subclasshook__, '__name__', '') == '__extrahook__' + ): + self.__subclasshook__ = _make_subclasshook(self) + + if origin and hasattr(origin, '__qualname__'): # Fix for Python 3.2. + self.__qualname__ = origin.__qualname__ + self.__tree_hash__ = (hash(self._subs_tree()) if origin else + super(GenericMeta, self).__hash__()) + return self + + def __init__(self, *args, **kwargs): + super(GenericMeta, self).__init__(*args, **kwargs) + if isinstance(self.__extra__, abc.ABCMeta): + self._abc_registry = self.__extra__._abc_registry + self._abc_cache = self.__extra__._abc_cache + elif self.__origin__ is not None: + self._abc_registry = self.__origin__._abc_registry + self._abc_cache = self.__origin__._abc_cache + + # _abc_negative_cache and _abc_negative_cache_version + # realized as descriptors, since GenClass[t1, t2, ...] always + # share subclass info with GenClass. + # This is an important memory optimization. + @property + def _abc_negative_cache(self): + if isinstance(self.__extra__, abc.ABCMeta): + return self.__extra__._abc_negative_cache + return self._gorg._abc_generic_negative_cache + + @_abc_negative_cache.setter + def _abc_negative_cache(self, value): + if self.__origin__ is None: + if isinstance(self.__extra__, abc.ABCMeta): + self.__extra__._abc_negative_cache = value + else: + self._abc_generic_negative_cache = value + + @property + def _abc_negative_cache_version(self): + if isinstance(self.__extra__, abc.ABCMeta): + return self.__extra__._abc_negative_cache_version + return self._gorg._abc_generic_negative_cache_version + + @_abc_negative_cache_version.setter + def _abc_negative_cache_version(self, value): + if self.__origin__ is None: + if isinstance(self.__extra__, abc.ABCMeta): + self.__extra__._abc_negative_cache_version = value + else: + self._abc_generic_negative_cache_version = value + + def _get_type_vars(self, tvars): + if self.__origin__ and self.__parameters__: + _get_type_vars(self.__parameters__, tvars) + + def _eval_type(self, globalns, localns): + ev_origin = (self.__origin__._eval_type(globalns, localns) + if self.__origin__ else None) + ev_args = tuple(_eval_type(a, globalns, localns) for a + in self.__args__) if self.__args__ else None + if ev_origin == self.__origin__ and ev_args == self.__args__: + return self + return self.__class__(self.__name__, + self.__bases__, + dict(self.__dict__), + tvars=_type_vars(ev_args) if ev_args else None, + args=ev_args, + origin=ev_origin, + extra=self.__extra__, + orig_bases=self.__orig_bases__) + + def __repr__(self): + if self.__origin__ is None: + return super(GenericMeta, self).__repr__() + return self._tree_repr(self._subs_tree()) + + def _tree_repr(self, tree): + arg_list = [] + for arg in tree[1:]: + if arg == (): + arg_list.append('()') + elif not isinstance(arg, tuple): + arg_list.append(_type_repr(arg)) + else: + arg_list.append(arg[0]._tree_repr(arg)) + return super(GenericMeta, self).__repr__() + '[%s]' % ', '.join(arg_list) + + def _subs_tree(self, tvars=None, args=None): + if self.__origin__ is None: + return self + tree_args = _subs_tree(self, tvars, args) + return (self._gorg,) + tuple(tree_args) + + def __eq__(self, other): + if not isinstance(other, GenericMeta): + return NotImplemented + if self.__origin__ is None or other.__origin__ is None: + return self is other + return self.__tree_hash__ == other.__tree_hash__ + + def __hash__(self): + return self.__tree_hash__ + + @_tp_cache + def __getitem__(self, params): + if not isinstance(params, tuple): + params = (params,) + if not params and self._gorg is not Tuple: + raise TypeError( + "Parameter list to %s[...] cannot be empty" % _qualname(self)) + msg = "Parameters to generic types must be types." + params = tuple(_type_check(p, msg) for p in params) + if self is Generic: + # Generic can only be subscripted with unique type variables. + if not all(isinstance(p, TypeVar) for p in params): + raise TypeError( + "Parameters to Generic[...] must all be type variables") + if len(set(params)) != len(params): + raise TypeError( + "Parameters to Generic[...] must all be unique") + tvars = params + args = params + elif self in (Tuple, Callable): + tvars = _type_vars(params) + args = params + elif self is _Protocol: + # _Protocol is internal, don't check anything. + tvars = params + args = params + elif self.__origin__ in (Generic, _Protocol): + # Can't subscript Generic[...] or _Protocol[...]. + raise TypeError("Cannot subscript already-subscripted %s" % + repr(self)) + else: + # Subscripting a regular Generic subclass. + _check_generic(self, params) + tvars = _type_vars(params) + args = params + + prepend = (self,) if self.__origin__ is None else () + return self.__class__(self.__name__, + prepend + self.__bases__, + dict(self.__dict__), + tvars=tvars, + args=args, + origin=self, + extra=self.__extra__, + orig_bases=self.__orig_bases__) + + def __subclasscheck__(self, cls): + if self.__origin__ is not None: + # This should only be modules within the standard + # library. singledispatch is the only exception, because + # it's a Python 2 backport of functools.singledispatch. + if sys._getframe(1).f_globals['__name__'] not in ['abc', 'functools', + 'singledispatch']: + raise TypeError("Parameterized generics cannot be used with class " + "or instance checks") + return False + if self is Generic: + raise TypeError("Class %r cannot be used with class " + "or instance checks" % self) + return super(GenericMeta, self).__subclasscheck__(cls) + + def __instancecheck__(self, instance): + # Since we extend ABC.__subclasscheck__ and + # ABC.__instancecheck__ inlines the cache checking done by the + # latter, we must extend __instancecheck__ too. For simplicity + # we just skip the cache check -- instance checks for generic + # classes are supposed to be rare anyways. + if not isinstance(instance, type): + return issubclass(instance.__class__, self) + return False + + def __setattr__(self, attr, value): + # We consider all the subscripted genrics as proxies for original class + if ( + attr.startswith('__') and attr.endswith('__') or + attr.startswith('_abc_') + ): + super(GenericMeta, self).__setattr__(attr, value) + else: + super(GenericMeta, self._gorg).__setattr__(attr, value) + + +def _copy_generic(self): + """Hack to work around https://bugs.python.org/issue11480 on Python 2""" + return self.__class__(self.__name__, self.__bases__, dict(self.__dict__), + self.__parameters__, self.__args__, self.__origin__, + self.__extra__, self.__orig_bases__) + + +copy._copy_dispatch[GenericMeta] = _copy_generic + + +# Prevent checks for Generic to crash when defining Generic. +Generic = None + + +def _generic_new(base_cls, cls, *args, **kwds): + # Assure type is erased on instantiation, + # but attempt to store it in __orig_class__ + if cls.__origin__ is None: + if (base_cls.__new__ is object.__new__ and + cls.__init__ is not object.__init__): + return base_cls.__new__(cls) + else: + return base_cls.__new__(cls, *args, **kwds) + else: + origin = cls._gorg + if (base_cls.__new__ is object.__new__ and + cls.__init__ is not object.__init__): + obj = base_cls.__new__(origin) + else: + obj = base_cls.__new__(origin, *args, **kwds) + try: + obj.__orig_class__ = cls + except AttributeError: + pass + obj.__init__(*args, **kwds) + return obj + + +class Generic(object): + """Abstract base class for generic types. + + A generic type is typically declared by inheriting from + this class parameterized with one or more type variables. + For example, a generic mapping type might be defined as:: + + class Mapping(Generic[KT, VT]): + def __getitem__(self, key: KT) -> VT: + ... + # Etc. + + This class can then be used as follows:: + + def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT: + try: + return mapping[key] + except KeyError: + return default + """ + + __metaclass__ = GenericMeta + __slots__ = () + + def __new__(cls, *args, **kwds): + if cls._gorg is Generic: + raise TypeError("Type Generic cannot be instantiated; " + "it can be used only as a base class") + return _generic_new(cls.__next_in_mro__, cls, *args, **kwds) + + +class _TypingEmpty(object): + """Internal placeholder for () or []. Used by TupleMeta and CallableMeta + to allow empty list/tuple in specific places, without allowing them + to sneak in where prohibited. + """ + + +class _TypingEllipsis(object): + """Internal placeholder for ... (ellipsis).""" + + +class TupleMeta(GenericMeta): + """Metaclass for Tuple (internal).""" + + @_tp_cache + def __getitem__(self, parameters): + if self.__origin__ is not None or self._gorg is not Tuple: + # Normal generic rules apply if this is not the first subscription + # or a subscription of a subclass. + return super(TupleMeta, self).__getitem__(parameters) + if parameters == (): + return super(TupleMeta, self).__getitem__((_TypingEmpty,)) + if not isinstance(parameters, tuple): + parameters = (parameters,) + if len(parameters) == 2 and parameters[1] is Ellipsis: + msg = "Tuple[t, ...]: t must be a type." + p = _type_check(parameters[0], msg) + return super(TupleMeta, self).__getitem__((p, _TypingEllipsis)) + msg = "Tuple[t0, t1, ...]: each t must be a type." + parameters = tuple(_type_check(p, msg) for p in parameters) + return super(TupleMeta, self).__getitem__(parameters) + + def __instancecheck__(self, obj): + if self.__args__ is None: + return isinstance(obj, tuple) + raise TypeError("Parameterized Tuple cannot be used " + "with isinstance().") + + def __subclasscheck__(self, cls): + if self.__args__ is None: + return issubclass(cls, tuple) + raise TypeError("Parameterized Tuple cannot be used " + "with issubclass().") + + +copy._copy_dispatch[TupleMeta] = _copy_generic + + +class Tuple(tuple): + """Tuple type; Tuple[X, Y] is the cross-product type of X and Y. + + Example: Tuple[T1, T2] is a tuple of two elements corresponding + to type variables T1 and T2. Tuple[int, float, str] is a tuple + of an int, a float and a string. + + To specify a variable-length tuple of homogeneous type, use Tuple[T, ...]. + """ + + __metaclass__ = TupleMeta + __extra__ = tuple + __slots__ = () + + def __new__(cls, *args, **kwds): + if cls._gorg is Tuple: + raise TypeError("Type Tuple cannot be instantiated; " + "use tuple() instead") + return _generic_new(tuple, cls, *args, **kwds) + + +class CallableMeta(GenericMeta): + """ Metaclass for Callable.""" + + def __repr__(self): + if self.__origin__ is None: + return super(CallableMeta, self).__repr__() + return self._tree_repr(self._subs_tree()) + + def _tree_repr(self, tree): + if self._gorg is not Callable: + return super(CallableMeta, self)._tree_repr(tree) + # For actual Callable (not its subclass) we override + # super(CallableMeta, self)._tree_repr() for nice formatting. + arg_list = [] + for arg in tree[1:]: + if not isinstance(arg, tuple): + arg_list.append(_type_repr(arg)) + else: + arg_list.append(arg[0]._tree_repr(arg)) + if arg_list[0] == '...': + return repr(tree[0]) + '[..., %s]' % arg_list[1] + return (repr(tree[0]) + + '[[%s], %s]' % (', '.join(arg_list[:-1]), arg_list[-1])) + + def __getitem__(self, parameters): + """A thin wrapper around __getitem_inner__ to provide the latter + with hashable arguments to improve speed. + """ + + if self.__origin__ is not None or self._gorg is not Callable: + return super(CallableMeta, self).__getitem__(parameters) + if not isinstance(parameters, tuple) or len(parameters) != 2: + raise TypeError("Callable must be used as " + "Callable[[arg, ...], result].") + args, result = parameters + if args is Ellipsis: + parameters = (Ellipsis, result) + else: + if not isinstance(args, list): + raise TypeError("Callable[args, result]: args must be a list." + " Got %.100r." % (args,)) + parameters = (tuple(args), result) + return self.__getitem_inner__(parameters) + + @_tp_cache + def __getitem_inner__(self, parameters): + args, result = parameters + msg = "Callable[args, result]: result must be a type." + result = _type_check(result, msg) + if args is Ellipsis: + return super(CallableMeta, self).__getitem__((_TypingEllipsis, result)) + msg = "Callable[[arg, ...], result]: each arg must be a type." + args = tuple(_type_check(arg, msg) for arg in args) + parameters = args + (result,) + return super(CallableMeta, self).__getitem__(parameters) + + +copy._copy_dispatch[CallableMeta] = _copy_generic + + +class Callable(object): + """Callable type; Callable[[int], str] is a function of (int) -> str. + + The subscription syntax must always be used with exactly two + values: the argument list and the return type. The argument list + must be a list of types or ellipsis; the return type must be a single type. + + There is no syntax to indicate optional or keyword arguments, + such function types are rarely used as callback types. + """ + + __metaclass__ = CallableMeta + __extra__ = collections_abc.Callable + __slots__ = () + + def __new__(cls, *args, **kwds): + if cls._gorg is Callable: + raise TypeError("Type Callable cannot be instantiated; " + "use a non-abstract subclass instead") + return _generic_new(cls.__next_in_mro__, cls, *args, **kwds) + + +def cast(typ, val): + """Cast a value to a type. + + This returns the value unchanged. To the type checker this + signals that the return value has the designated type, but at + runtime we intentionally don't check anything (we want this + to be as fast as possible). + """ + return val + + +def _get_defaults(func): + """Internal helper to extract the default arguments, by name.""" + code = func.__code__ + pos_count = code.co_argcount + arg_names = code.co_varnames + arg_names = arg_names[:pos_count] + defaults = func.__defaults__ or () + kwdefaults = func.__kwdefaults__ + res = dict(kwdefaults) if kwdefaults else {} + pos_offset = pos_count - len(defaults) + for name, value in zip(arg_names[pos_offset:], defaults): + assert name not in res + res[name] = value + return res + + +def get_type_hints(obj, globalns=None, localns=None): + """In Python 2 this is not supported and always returns None.""" + return None + + +def no_type_check(arg): + """Decorator to indicate that annotations are not type hints. + + The argument must be a class or function; if it is a class, it + applies recursively to all methods and classes defined in that class + (but not to methods defined in its superclasses or subclasses). + + This mutates the function(s) or class(es) in place. + """ + if isinstance(arg, type): + arg_attrs = arg.__dict__.copy() + for attr, val in arg.__dict__.items(): + if val in arg.__bases__ + (arg,): + arg_attrs.pop(attr) + for obj in arg_attrs.values(): + if isinstance(obj, types.FunctionType): + obj.__no_type_check__ = True + if isinstance(obj, type): + no_type_check(obj) + try: + arg.__no_type_check__ = True + except TypeError: # built-in classes + pass + return arg + + +def no_type_check_decorator(decorator): + """Decorator to give another decorator the @no_type_check effect. + + This wraps the decorator with something that wraps the decorated + function in @no_type_check. + """ + + @functools.wraps(decorator) + def wrapped_decorator(*args, **kwds): + func = decorator(*args, **kwds) + func = no_type_check(func) + return func + + return wrapped_decorator + + +def _overload_dummy(*args, **kwds): + """Helper for @overload to raise when called.""" + raise NotImplementedError( + "You should not call an overloaded function. " + "A series of @overload-decorated functions " + "outside a stub module should always be followed " + "by an implementation that is not @overload-ed.") + + +def overload(func): + """Decorator for overloaded functions/methods. + + In a stub file, place two or more stub definitions for the same + function in a row, each decorated with @overload. For example: + + @overload + def utf8(value: None) -> None: ... + @overload + def utf8(value: bytes) -> bytes: ... + @overload + def utf8(value: str) -> bytes: ... + + In a non-stub file (i.e. a regular .py file), do the same but + follow it with an implementation. The implementation should *not* + be decorated with @overload. For example: + + @overload + def utf8(value: None) -> None: ... + @overload + def utf8(value: bytes) -> bytes: ... + @overload + def utf8(value: str) -> bytes: ... + def utf8(value): + # implementation goes here + """ + return _overload_dummy + + +class _ProtocolMeta(GenericMeta): + """Internal metaclass for _Protocol. + + This exists so _Protocol classes can be generic without deriving + from Generic. + """ + + def __instancecheck__(self, obj): + if _Protocol not in self.__bases__: + return super(_ProtocolMeta, self).__instancecheck__(obj) + raise TypeError("Protocols cannot be used with isinstance().") + + def __subclasscheck__(self, cls): + if not self._is_protocol: + # No structural checks since this isn't a protocol. + return NotImplemented + + if self is _Protocol: + # Every class is a subclass of the empty protocol. + return True + + # Find all attributes defined in the protocol. + attrs = self._get_protocol_attrs() + + for attr in attrs: + if not any(attr in d.__dict__ for d in cls.__mro__): + return False + return True + + def _get_protocol_attrs(self): + # Get all Protocol base classes. + protocol_bases = [] + for c in self.__mro__: + if getattr(c, '_is_protocol', False) and c.__name__ != '_Protocol': + protocol_bases.append(c) + + # Get attributes included in protocol. + attrs = set() + for base in protocol_bases: + for attr in base.__dict__.keys(): + # Include attributes not defined in any non-protocol bases. + for c in self.__mro__: + if (c is not base and attr in c.__dict__ and + not getattr(c, '_is_protocol', False)): + break + else: + if (not attr.startswith('_abc_') and + attr != '__abstractmethods__' and + attr != '_is_protocol' and + attr != '_gorg' and + attr != '__dict__' and + attr != '__args__' and + attr != '__slots__' and + attr != '_get_protocol_attrs' and + attr != '__next_in_mro__' and + attr != '__parameters__' and + attr != '__origin__' and + attr != '__orig_bases__' and + attr != '__extra__' and + attr != '__tree_hash__' and + attr != '__module__'): + attrs.add(attr) + + return attrs + + +class _Protocol(object): + """Internal base class for protocol classes. + + This implements a simple-minded structural issubclass check + (similar but more general than the one-offs in collections.abc + such as Hashable). + """ + + __metaclass__ = _ProtocolMeta + __slots__ = () + + _is_protocol = True + + +# Various ABCs mimicking those in collections.abc. +# A few are simply re-exported for completeness. + +Hashable = collections_abc.Hashable # Not generic. + + +class Iterable(Generic[T_co]): + __slots__ = () + __extra__ = collections_abc.Iterable + + +class Iterator(Iterable[T_co]): + __slots__ = () + __extra__ = collections_abc.Iterator + + +class SupportsInt(_Protocol): + __slots__ = () + + @abstractmethod + def __int__(self): + pass + + +class SupportsFloat(_Protocol): + __slots__ = () + + @abstractmethod + def __float__(self): + pass + + +class SupportsComplex(_Protocol): + __slots__ = () + + @abstractmethod + def __complex__(self): + pass + + +class SupportsAbs(_Protocol[T_co]): + __slots__ = () + + @abstractmethod + def __abs__(self): + pass + + +if hasattr(collections_abc, 'Reversible'): + class Reversible(Iterable[T_co]): + __slots__ = () + __extra__ = collections_abc.Reversible +else: + class Reversible(_Protocol[T_co]): + __slots__ = () + + @abstractmethod + def __reversed__(self): + pass + + +Sized = collections_abc.Sized # Not generic. + + +class Container(Generic[T_co]): + __slots__ = () + __extra__ = collections_abc.Container + + +# Callable was defined earlier. + + +class AbstractSet(Sized, Iterable[T_co], Container[T_co]): + __slots__ = () + __extra__ = collections_abc.Set + + +class MutableSet(AbstractSet[T]): + __slots__ = () + __extra__ = collections_abc.MutableSet + + +# NOTE: It is only covariant in the value type. +class Mapping(Sized, Iterable[KT], Container[KT], Generic[KT, VT_co]): + __slots__ = () + __extra__ = collections_abc.Mapping + + +class MutableMapping(Mapping[KT, VT]): + __slots__ = () + __extra__ = collections_abc.MutableMapping + + +if hasattr(collections_abc, 'Reversible'): + class Sequence(Sized, Reversible[T_co], Container[T_co]): + __slots__ = () + __extra__ = collections_abc.Sequence +else: + class Sequence(Sized, Iterable[T_co], Container[T_co]): + __slots__ = () + __extra__ = collections_abc.Sequence + + +class MutableSequence(Sequence[T]): + __slots__ = () + __extra__ = collections_abc.MutableSequence + + +class ByteString(Sequence[int]): + pass + + +ByteString.register(str) +ByteString.register(bytearray) + + +class List(list, MutableSequence[T]): + __slots__ = () + __extra__ = list + + def __new__(cls, *args, **kwds): + if cls._gorg is List: + raise TypeError("Type List cannot be instantiated; " + "use list() instead") + return _generic_new(list, cls, *args, **kwds) + + +class Deque(collections.deque, MutableSequence[T]): + __slots__ = () + __extra__ = collections.deque + + def __new__(cls, *args, **kwds): + if cls._gorg is Deque: + return collections.deque(*args, **kwds) + return _generic_new(collections.deque, cls, *args, **kwds) + + +class Set(set, MutableSet[T]): + __slots__ = () + __extra__ = set + + def __new__(cls, *args, **kwds): + if cls._gorg is Set: + raise TypeError("Type Set cannot be instantiated; " + "use set() instead") + return _generic_new(set, cls, *args, **kwds) + + +class FrozenSet(frozenset, AbstractSet[T_co]): + __slots__ = () + __extra__ = frozenset + + def __new__(cls, *args, **kwds): + if cls._gorg is FrozenSet: + raise TypeError("Type FrozenSet cannot be instantiated; " + "use frozenset() instead") + return _generic_new(frozenset, cls, *args, **kwds) + + +class MappingView(Sized, Iterable[T_co]): + __slots__ = () + __extra__ = collections_abc.MappingView + + +class KeysView(MappingView[KT], AbstractSet[KT]): + __slots__ = () + __extra__ = collections_abc.KeysView + + +class ItemsView(MappingView[Tuple[KT, VT_co]], + AbstractSet[Tuple[KT, VT_co]], + Generic[KT, VT_co]): + __slots__ = () + __extra__ = collections_abc.ItemsView + + +class ValuesView(MappingView[VT_co]): + __slots__ = () + __extra__ = collections_abc.ValuesView + + +class ContextManager(Generic[T_co]): + __slots__ = () + + def __enter__(self): + return self + + @abc.abstractmethod + def __exit__(self, exc_type, exc_value, traceback): + return None + + @classmethod + def __subclasshook__(cls, C): + if cls is ContextManager: + # In Python 3.6+, it is possible to set a method to None to + # explicitly indicate that the class does not implement an ABC + # (https://bugs.python.org/issue25958), but we do not support + # that pattern here because this fallback class is only used + # in Python 3.5 and earlier. + if (any("__enter__" in B.__dict__ for B in C.__mro__) and + any("__exit__" in B.__dict__ for B in C.__mro__)): + return True + return NotImplemented + + +class Dict(dict, MutableMapping[KT, VT]): + __slots__ = () + __extra__ = dict + + def __new__(cls, *args, **kwds): + if cls._gorg is Dict: + raise TypeError("Type Dict cannot be instantiated; " + "use dict() instead") + return _generic_new(dict, cls, *args, **kwds) + + +class DefaultDict(collections.defaultdict, MutableMapping[KT, VT]): + __slots__ = () + __extra__ = collections.defaultdict + + def __new__(cls, *args, **kwds): + if cls._gorg is DefaultDict: + return collections.defaultdict(*args, **kwds) + return _generic_new(collections.defaultdict, cls, *args, **kwds) + + +class Counter(collections.Counter, Dict[T, int]): + __slots__ = () + __extra__ = collections.Counter + + def __new__(cls, *args, **kwds): + if cls._gorg is Counter: + return collections.Counter(*args, **kwds) + return _generic_new(collections.Counter, cls, *args, **kwds) + + +# Determine what base class to use for Generator. +if hasattr(collections_abc, 'Generator'): + # Sufficiently recent versions of 3.5 have a Generator ABC. + _G_base = collections_abc.Generator +else: + # Fall back on the exact type. + _G_base = types.GeneratorType + + +class Generator(Iterator[T_co], Generic[T_co, T_contra, V_co]): + __slots__ = () + __extra__ = _G_base + + def __new__(cls, *args, **kwds): + if cls._gorg is Generator: + raise TypeError("Type Generator cannot be instantiated; " + "create a subclass instead") + return _generic_new(_G_base, cls, *args, **kwds) + + +# Internal type variable used for Type[]. +CT_co = TypeVar('CT_co', covariant=True, bound=type) + + +# This is not a real generic class. Don't use outside annotations. +class Type(Generic[CT_co]): + """A special construct usable to annotate class objects. + + For example, suppose we have the following classes:: + + class User: ... # Abstract base for User classes + class BasicUser(User): ... + class ProUser(User): ... + class TeamUser(User): ... + + And a function that takes a class argument that's a subclass of + User and returns an instance of the corresponding class:: + + U = TypeVar('U', bound=User) + def new_user(user_class: Type[U]) -> U: + user = user_class() + # (Here we could write the user object to a database) + return user + + joe = new_user(BasicUser) + + At this point the type checker knows that joe has type BasicUser. + """ + __slots__ = () + __extra__ = type + + +def NamedTuple(typename, fields): + """Typed version of namedtuple. + + Usage:: + + Employee = typing.NamedTuple('Employee', [('name', str), ('id', int)]) + + This is equivalent to:: + + Employee = collections.namedtuple('Employee', ['name', 'id']) + + The resulting class has one extra attribute: _field_types, + giving a dict mapping field names to types. (The field names + are in the _fields attribute, which is part of the namedtuple + API.) + """ + fields = [(n, t) for n, t in fields] + cls = collections.namedtuple(typename, [n for n, t in fields]) + cls._field_types = dict(fields) + # Set the module to the caller's module (otherwise it'd be 'typing'). + try: + cls.__module__ = sys._getframe(1).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): + pass + return cls + + +def NewType(name, tp): + """NewType creates simple unique types with almost zero + runtime overhead. NewType(name, tp) is considered a subtype of tp + by static type checkers. At runtime, NewType(name, tp) returns + a dummy function that simply returns its argument. Usage:: + + UserId = NewType('UserId', int) + + def name_by_id(user_id): + # type: (UserId) -> str + ... + + UserId('user') # Fails type check + + name_by_id(42) # Fails type check + name_by_id(UserId(42)) # OK + + num = UserId(5) + 1 # type: int + """ + + def new_type(x): + return x + + # Some versions of Python 2 complain because of making all strings unicode + new_type.__name__ = str(name) + new_type.__supertype__ = tp + return new_type + + +# Python-version-specific alias (Python 2: unicode; Python 3: str) +Text = unicode + + +# Constant that's True when type checking, but False here. +TYPE_CHECKING = False + + +class IO(Generic[AnyStr]): + """Generic base class for TextIO and BinaryIO. + + This is an abstract, generic version of the return of open(). + + NOTE: This does not distinguish between the different possible + classes (text vs. binary, read vs. write vs. read/write, + append-only, unbuffered). The TextIO and BinaryIO subclasses + below capture the distinctions between text vs. binary, which is + pervasive in the interface; however we currently do not offer a + way to track the other distinctions in the type system. + """ + + __slots__ = () + + @abstractproperty + def mode(self): + pass + + @abstractproperty + def name(self): + pass + + @abstractmethod + def close(self): + pass + + @abstractproperty + def closed(self): + pass + + @abstractmethod + def fileno(self): + pass + + @abstractmethod + def flush(self): + pass + + @abstractmethod + def isatty(self): + pass + + @abstractmethod + def read(self, n=-1): + pass + + @abstractmethod + def readable(self): + pass + + @abstractmethod + def readline(self, limit=-1): + pass + + @abstractmethod + def readlines(self, hint=-1): + pass + + @abstractmethod + def seek(self, offset, whence=0): + pass + + @abstractmethod + def seekable(self): + pass + + @abstractmethod + def tell(self): + pass + + @abstractmethod + def truncate(self, size=None): + pass + + @abstractmethod + def writable(self): + pass + + @abstractmethod + def write(self, s): + pass + + @abstractmethod + def writelines(self, lines): + pass + + @abstractmethod + def __enter__(self): + pass + + @abstractmethod + def __exit__(self, type, value, traceback): + pass + + +class BinaryIO(IO[bytes]): + """Typed version of the return of open() in binary mode.""" + + __slots__ = () + + @abstractmethod + def write(self, s): + pass + + @abstractmethod + def __enter__(self): + pass + + +class TextIO(IO[unicode]): + """Typed version of the return of open() in text mode.""" + + __slots__ = () + + @abstractproperty + def buffer(self): + pass + + @abstractproperty + def encoding(self): + pass + + @abstractproperty + def errors(self): + pass + + @abstractproperty + def line_buffering(self): + pass + + @abstractproperty + def newlines(self): + pass + + @abstractmethod + def __enter__(self): + pass + + +class io(object): + """Wrapper namespace for IO generic classes.""" + + __all__ = ['IO', 'TextIO', 'BinaryIO'] + IO = IO + TextIO = TextIO + BinaryIO = BinaryIO + + +io.__name__ = __name__ + b'.io' +sys.modules[io.__name__] = io + + +Pattern = _TypeAlias('Pattern', AnyStr, type(stdlib_re.compile('')), + lambda p: p.pattern) +Match = _TypeAlias('Match', AnyStr, type(stdlib_re.match('', '')), + lambda m: m.re.pattern) + + +class re(object): + """Wrapper namespace for re type aliases.""" + + __all__ = ['Pattern', 'Match'] + Pattern = Pattern + Match = Match + + +re.__name__ = __name__ + b'.re' +sys.modules[re.__name__] = re diff --git a/sources/shiboken2/tests/libsample/CMakeLists.txt b/sources/shiboken2/tests/libsample/CMakeLists.txt index 7bbc0c3dd..ae3d40312 100644 --- a/sources/shiboken2/tests/libsample/CMakeLists.txt +++ b/sources/shiboken2/tests/libsample/CMakeLists.txt @@ -10,6 +10,7 @@ complex.cpp onlycopy.cpp derived.cpp echo.cpp +exceptiontest.cpp functions.cpp handle.cpp implicitconv.cpp diff --git a/sources/shiboken2/tests/libsample/exceptiontest.cpp b/sources/shiboken2/tests/libsample/exceptiontest.cpp new file mode 100644 index 000000000..1302a8e43 --- /dev/null +++ b/sources/shiboken2/tests/libsample/exceptiontest.cpp @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite 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 "exceptiontest.h" + +class TestException : public std::exception +{ +public: + const char *what() const noexcept override + { return "TestException"; } +}; + +ExceptionTest::ExceptionTest() = default; + +int ExceptionTest::intThrowStdException(bool doThrow) +{ + if (doThrow) + throw TestException(); + return 1; +} + +void ExceptionTest::voidThrowStdException(bool doThrow) +{ + if (doThrow) + throw TestException(); +} + +int ExceptionTest::intThrowInt(bool doThrow) +{ + if (doThrow) + throw 42; + return 1; +} + +void ExceptionTest::voidThrowInt(bool doThrow) +{ + if (doThrow) + throw 42; +} diff --git a/sources/shiboken2/tests/libsample/exceptiontest.h b/sources/shiboken2/tests/libsample/exceptiontest.h new file mode 100644 index 000000000..8ab3e2b67 --- /dev/null +++ b/sources/shiboken2/tests/libsample/exceptiontest.h @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite 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 EXCEPTIONTEST_H +#define EXCEPTIONTEST_H + +#include "libsamplemacros.h" + +#include <exception> + +class LIBSAMPLE_API ExceptionTest +{ + public: + ExceptionTest(); + + int intThrowStdException(bool doThrow); + void voidThrowStdException(bool doThrow); + + int intThrowInt(bool doThrow); + void voidThrowInt(bool doThrow); +}; + +#endif // EXCEPTIONTEST_H diff --git a/sources/shiboken2/tests/libsample/mapuser.cpp b/sources/shiboken2/tests/libsample/mapuser.cpp index 1dbd02d26..89a835af8 100644 --- a/sources/shiboken2/tests/libsample/mapuser.cpp +++ b/sources/shiboken2/tests/libsample/mapuser.cpp @@ -67,3 +67,8 @@ MapUser::showMap(std::map<std::string, int> mapping) cout << (*it).first << " => " << (*it).second << endl; } +std::map<int, std::list<std::list<double> > > MapUser::foo() const +{ + std::map<int, std::list<std::list<double> > > result; + return result; +} diff --git a/sources/shiboken2/tests/libsample/mapuser.h b/sources/shiboken2/tests/libsample/mapuser.h index 9677d2df2..ad434b957 100644 --- a/sources/shiboken2/tests/libsample/mapuser.h +++ b/sources/shiboken2/tests/libsample/mapuser.h @@ -58,6 +58,8 @@ public: inline const std::map<int, ByteArray>& passMapIntValueType(const std::map<int, ByteArray>& arg) { return arg; } + std::map<int, std::list<std::list<double> > > foo() const; + private: std::map<std::string, std::list<int> > m_map; }; diff --git a/sources/shiboken2/tests/libsample/nontypetemplate.h b/sources/shiboken2/tests/libsample/nontypetemplate.h new file mode 100644 index 000000000..4e2100626 --- /dev/null +++ b/sources/shiboken2/tests/libsample/nontypetemplate.h @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite 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 NONTYPETEMPLATE_H +#define NONTYPETEMPLATE_H + +#include "libsamplemacros.h" + +#include <algorithm> +#include <numeric> + +template <int Size> class IntArray +{ +public: + explicit IntArray(int v) { std::fill(m_array, m_array + Size, v); } + + int sum() const { return std::accumulate(m_array, m_array + Size, int(0)); } + +private: + int m_array[Size]; +}; + +typedef IntArray<2> IntArray2; +typedef IntArray<3> IntArray3; + +#endif // NONTYPETEMPLATE_H diff --git a/sources/shiboken2/tests/libsample/objecttype.cpp b/sources/shiboken2/tests/libsample/objecttype.cpp index e09a92f47..f82b7cf0d 100644 --- a/sources/shiboken2/tests/libsample/objecttype.cpp +++ b/sources/shiboken2/tests/libsample/objecttype.cpp @@ -282,6 +282,7 @@ void ObjectType::callVirtualCreateChild() ObjectType* fake_parent = new ObjectType(); ObjectType* fake_child = createChild(fake_parent); assert(fake_child->isPython()); + (void)fake_child; delete fake_parent; } diff --git a/sources/shiboken2/tests/libsample/objecttype.h b/sources/shiboken2/tests/libsample/objecttype.h index bcb4f3332..ecd67b684 100644 --- a/sources/shiboken2/tests/libsample/objecttype.h +++ b/sources/shiboken2/tests/libsample/objecttype.h @@ -63,7 +63,7 @@ private: class ObjectTypeLayout; class ObjectType; -typedef std::list<ObjectType*> ObjectTypeList; +using ObjectTypeList = std::list<ObjectType*>; class LIBSAMPLE_API ObjectType { diff --git a/sources/shiboken2/tests/libsample/samplenamespace.cpp b/sources/shiboken2/tests/libsample/samplenamespace.cpp index ec3da3dbf..e066869d2 100644 --- a/sources/shiboken2/tests/libsample/samplenamespace.cpp +++ b/sources/shiboken2/tests/libsample/samplenamespace.cpp @@ -36,6 +36,13 @@ using namespace std; namespace SampleNamespace { +// PYSIDE-817, scoped enums must not be converted to int in the wrappers generated +// for the protected hacks +SomeClass::PublicScopedEnum SomeClass::protectedMethodReturningPublicScopedEnum() const +{ + return PublicScopedEnum::v1; +} + OutValue enumInEnumOut(InValue in) { diff --git a/sources/shiboken2/tests/libsample/samplenamespace.h b/sources/shiboken2/tests/libsample/samplenamespace.h index 37a445e51..27fa11290 100644 --- a/sources/shiboken2/tests/libsample/samplenamespace.h +++ b/sources/shiboken2/tests/libsample/samplenamespace.h @@ -96,9 +96,11 @@ LIBSAMPLE_API void doSomethingWithArray(const unsigned char* data, unsigned int LIBSAMPLE_API int enumItemAsDefaultValueToIntArgument(int value = ZeroIn); -class SomeClass +class LIBSAMPLE_API SomeClass { public: + enum class PublicScopedEnum { v1, v2 }; + class SomeInnerClass { public: @@ -131,6 +133,8 @@ protected: ProtectedItem0, ProtectedItem1 }; + + PublicScopedEnum protectedMethodReturningPublicScopedEnum() const; }; class DerivedFromNamespace : public SomeClass::SomeInnerClass::OkThisIsRecursiveEnough diff --git a/sources/shiboken2/tests/minimalbinding/CMakeLists.txt b/sources/shiboken2/tests/minimalbinding/CMakeLists.txt index b8b6417d1..ec674b56b 100644 --- a/sources/shiboken2/tests/minimalbinding/CMakeLists.txt +++ b/sources/shiboken2/tests/minimalbinding/CMakeLists.txt @@ -15,7 +15,9 @@ ${CMAKE_CURRENT_BINARY_DIR}/minimal/minbooluser_wrapper.cpp configure_file("${CMAKE_CURRENT_SOURCE_DIR}/minimal-binding.txt.in" "${CMAKE_CURRENT_BINARY_DIR}/minimal-binding.txt" @ONLY) -add_custom_command(OUTPUT ${minimal_SRC} +add_custom_command( +OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/mjb_rejected_classes.log" +BYPRODUCTS ${minimal_SRC} COMMAND shiboken2 --project-file=${CMAKE_CURRENT_BINARY_DIR}/minimal-binding.txt ${GENERATOR_EXTRA_FLAGS} DEPENDS ${minimal_TYPESYSTEM} ${CMAKE_CURRENT_SOURCE_DIR}/global.h shiboken2 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} @@ -38,3 +40,4 @@ target_link_libraries(minimal libminimal ${SBK_PYTHON_LIBRARIES} libshiboken) +create_generator_target(minimal) diff --git a/sources/shiboken2/tests/otherbinding/CMakeLists.txt b/sources/shiboken2/tests/otherbinding/CMakeLists.txt index 186766b41..0be66f797 100644 --- a/sources/shiboken2/tests/otherbinding/CMakeLists.txt +++ b/sources/shiboken2/tests/otherbinding/CMakeLists.txt @@ -17,7 +17,9 @@ ${CMAKE_CURRENT_BINARY_DIR}/other/other_module_wrapper.cpp configure_file("${CMAKE_CURRENT_SOURCE_DIR}/other-binding.txt.in" "${CMAKE_CURRENT_BINARY_DIR}/other-binding.txt" @ONLY) -add_custom_command(OUTPUT ${other_SRC} +add_custom_command( +OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/mjb_rejected_classes.log" +BYPRODUCTS ${other_SRC} COMMAND shiboken2 --project-file=${CMAKE_CURRENT_BINARY_DIR}/other-binding.txt ${GENERATOR_EXTRA_FLAGS} DEPENDS ${other_TYPESYSTEM} ${CMAKE_CURRENT_SOURCE_DIR}/global.h shiboken2 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} @@ -48,4 +50,5 @@ target_link_libraries(other libshiboken) add_dependencies(other sample) +create_generator_target(other) diff --git a/sources/shiboken2/tests/otherbinding/extended_multiply_operator_test.py b/sources/shiboken2/tests/otherbinding/extended_multiply_operator_test.py index 08541a1f4..0c58fbf5b 100755 --- a/sources/shiboken2/tests/otherbinding/extended_multiply_operator_test.py +++ b/sources/shiboken2/tests/otherbinding/extended_multiply_operator_test.py @@ -57,7 +57,7 @@ class PointOperationsWithNumber(unittest.TestCase): '''sample.Point * other.Number''' pt = Point(2, 7) num = Number(11) - self.assertEqual(pt * num, pt * 11) + self.assertEqual(pt * num.value(), pt * 11) if __name__ == '__main__': unittest.main() diff --git a/sources/shiboken2/tests/samplebinding/CMakeLists.txt b/sources/shiboken2/tests/samplebinding/CMakeLists.txt index 32117e44a..7f4bec5f4 100644 --- a/sources/shiboken2/tests/samplebinding/CMakeLists.txt +++ b/sources/shiboken2/tests/samplebinding/CMakeLists.txt @@ -29,11 +29,14 @@ ${CMAKE_CURRENT_BINARY_DIR}/sample/derived_someinnerclass_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/echo_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/event_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/expression_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/exceptiontest_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/friendofonlycopy_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/handleholder_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/implicitconv_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/implicitbase_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/implicittarget_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/intarray2_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/intarray3_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/intlist_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/sortedoverload_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/intwrapper_wrapper.cpp @@ -125,7 +128,9 @@ ${CMAKE_CURRENT_BINARY_DIR}/sample/union_wrapper.cpp configure_file("${CMAKE_CURRENT_SOURCE_DIR}/sample-binding.txt.in" "${CMAKE_CURRENT_BINARY_DIR}/sample-binding.txt" @ONLY) -add_custom_command(OUTPUT ${sample_SRC} +add_custom_command( +OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/mjb_rejected_classes.log" +BYPRODUCTS ${sample_SRC} COMMAND shiboken2 --project-file=${CMAKE_CURRENT_BINARY_DIR}/sample-binding.txt ${GENERATOR_EXTRA_FLAGS} DEPENDS ${sample_TYPESYSTEM} ${CMAKE_CURRENT_SOURCE_DIR}/global.h shiboken2 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} @@ -149,3 +154,4 @@ target_link_libraries(sample libsample ${SBK_PYTHON_LIBRARIES} libshiboken) +create_generator_target(sample) diff --git a/sources/shiboken2/tests/samplebinding/exception_test.py b/sources/shiboken2/tests/samplebinding/exception_test.py new file mode 100644 index 000000000..d6c02433a --- /dev/null +++ b/sources/shiboken2/tests/samplebinding/exception_test.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python +# +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the test suite 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$ +## +############################################################################# + +import unittest + +from sample import ExceptionTest + +class CppExceptionTest(unittest.TestCase): + + def testVoid(self): + exceptionCount = 0 + et = ExceptionTest() + + et.voidThrowStdException(False) + + try: + et.voidThrowStdException(True) + except: + exceptionCount += 1 + + et.voidThrowInt(False) + + try: + et.voidThrowInt(True) + except: + exceptionCount += 1 + + self.assertEqual(exceptionCount, 2) + + def testReturnValue(self): + exceptionCount = 0 + et = ExceptionTest() + + result = et.intThrowStdException(False); + + try: + result = et.intThrowStdException(True); + except: + exceptionCount += 1 + + result = et.intThrowInt(False); + + try: + result = et.intThrowInt(True); + except: + exceptionCount += 1 + + self.assertEqual(exceptionCount, 2) + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken2/tests/samplebinding/global.h b/sources/shiboken2/tests/samplebinding/global.h index f2add93ff..3984102a8 100644 --- a/sources/shiboken2/tests/samplebinding/global.h +++ b/sources/shiboken2/tests/samplebinding/global.h @@ -37,8 +37,10 @@ #include "sbkdate.h" #include "derived.h" #include "echo.h" +#include "exceptiontest.h" #include "functions.h" #include "implicitconv.h" +#include "nontypetemplate.h" #include "overloadsort.h" #include "handle.h" #include "injectcode.h" diff --git a/sources/shiboken2/tests/samplebinding/nontypetemplate_test.py b/sources/shiboken2/tests/samplebinding/nontypetemplate_test.py new file mode 100644 index 000000000..9adfa2441 --- /dev/null +++ b/sources/shiboken2/tests/samplebinding/nontypetemplate_test.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +# +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the test suite 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$ +## +############################################################################# + +import unittest + +from sample import IntArray2, IntArray3 + +class NonTypeTemplateTest(unittest.TestCase): + + def testNonTypeTemplate(self): + array2 = IntArray2(3) + self.assertEqual(array2.sum(), 6) + array3 = IntArray3(5) + self.assertEqual(array3.sum(), 15) + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken2/tests/samplebinding/typesystem_sample.xml b/sources/shiboken2/tests/samplebinding/typesystem_sample.xml index 00052e881..9b967fc53 100644 --- a/sources/shiboken2/tests/samplebinding/typesystem_sample.xml +++ b/sources/shiboken2/tests/samplebinding/typesystem_sample.xml @@ -520,6 +520,10 @@ <suppress-warning text="skipping function 'ClassWithFunctionPointer::callFunctionPointer', unmatched parameter type 'void (*)(void*)'" /> </value-type> + <value-type name="IntArray" generate="no"/> + <value-type name="IntArray2"/> + <value-type name="IntArray3"/> + <enum-type name="OverloadedFuncEnum"/> <!-- BUG: renaming the ICOverloadedFuncEnum to the same name @@ -545,6 +549,7 @@ <enum-type name="SampleNamespace"/> </object-type> <value-type name="SomeClass"> + <enum-type name="PublicScopedEnum"/> <value-type name="SomeInnerClass"> <object-type name="OkThisIsRecursiveEnough"> <enum-type name="NiceEnum" /> @@ -1964,7 +1969,7 @@ <define-ownership owner="c++"/> </modify-argument> </modify-function> - <modify-function signature="acceptSequence(const char*[])"> + <modify-function signature="acceptSequence(const char*const[])"> <modify-argument index="1"> <replace-type modified-type="PySequence" /> <conversion-rule class="native"> @@ -2388,6 +2393,8 @@ <value-type name="Expression" /> + <object-type name="ExceptionTest" exception-handling="auto-on"/> + <value-type name="ModelIndex" /> <value-type name="ReferentModelIndex"> <modify-function signature="operator const ModelIndex&()const"> diff --git a/sources/shiboken2/tests/smartbinding/CMakeLists.txt b/sources/shiboken2/tests/smartbinding/CMakeLists.txt index faaa797b6..43888fae2 100644 --- a/sources/shiboken2/tests/smartbinding/CMakeLists.txt +++ b/sources/shiboken2/tests/smartbinding/CMakeLists.txt @@ -16,7 +16,9 @@ ${CMAKE_CURRENT_BINARY_DIR}/smart/registry_wrapper.cpp configure_file("${CMAKE_CURRENT_SOURCE_DIR}/smart-binding.txt.in" "${CMAKE_CURRENT_BINARY_DIR}/smart-binding.txt" @ONLY) -add_custom_command(OUTPUT ${smart_SRC} +add_custom_command( +OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/mjb_rejected_classes.log" +BYPRODUCTS ${smart_SRC} COMMAND shiboken2 --project-file=${CMAKE_CURRENT_BINARY_DIR}/smart-binding.txt ${GENERATOR_EXTRA_FLAGS} DEPENDS ${smart_TYPESYSTEM} ${CMAKE_CURRENT_SOURCE_DIR}/global.h shiboken2 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} @@ -40,3 +42,4 @@ target_link_libraries(smart libsmart ${SBK_PYTHON_LIBRARIES} libshiboken) +create_generator_target(smart) |