diff options
Diffstat (limited to 'sources/shiboken6')
68 files changed, 1252 insertions, 631 deletions
diff --git a/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp b/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp index 89d636964..f07fb96c6 100644 --- a/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp +++ b/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp @@ -81,7 +81,7 @@ QTextStream &operator<<(QTextStream &str, const RejectEntry &re) return str; } -static void applyCachedFunctionModifications(AbstractMetaFunction *metaFunction, +static void applyCachedFunctionModifications(const AbstractMetaFunctionPtr &metaFunction, const FunctionModificationList &functionMods) { for (const FunctionModification &mod : functionMods) { @@ -317,7 +317,7 @@ void AbstractMetaBuilderPrivate::traverseOperatorFunction(const FunctionModelIte return; } - AbstractMetaFunction *metaFunction = traverseFunction(item, baseoperandClass); + auto metaFunction = traverseFunction(item, baseoperandClass); if (metaFunction == nullptr) return; @@ -350,7 +350,7 @@ void AbstractMetaBuilderPrivate::traverseOperatorFunction(const FunctionModelIte } metaFunction->setFlags(flags); metaFunction->setAccess(Access::Public); - AbstractMetaClass::addFunction(baseoperandClass, AbstractMetaFunctionCPtr(metaFunction)); + AbstractMetaClass::addFunction(baseoperandClass, metaFunction); if (!metaFunction->arguments().isEmpty()) { const auto include = metaFunction->arguments().constFirst().type().typeEntry()->include(); baseoperandClass->typeEntry()->addArgumentInclude(include); @@ -371,7 +371,7 @@ bool AbstractMetaBuilderPrivate::traverseStreamOperator(const FunctionModelItem if (streamedClass == nullptr) return false; - AbstractMetaFunction *streamFunction = traverseFunction(item, streamedClass); + auto streamFunction = traverseFunction(item, streamedClass); if (!streamFunction) return false; @@ -401,7 +401,7 @@ bool AbstractMetaBuilderPrivate::traverseStreamOperator(const FunctionModelItem funcClass = streamClass; } - AbstractMetaClass::addFunction(funcClass, AbstractMetaFunctionCPtr(streamFunction)); + AbstractMetaClass::addFunction(funcClass, streamFunction); auto funcTe = funcClass->typeEntry(); if (funcClass == streamClass) funcTe->addArgumentInclude(streamedClass->typeEntry()->include()); @@ -461,7 +461,7 @@ FileModelItem AbstractMetaBuilderPrivate::buildDom(QByteArrayList arguments, + clang::languageLevelOption(level)); } FileModelItem result = clang::parse(arguments, addCompilerSupportArguments, - clangFlags, builder) + level, clangFlags, builder) ? builder.dom() : FileModelItem(); const clang::BaseVisitor::Diagnostics &diagnostics = builder.diagnostics(); if (const auto diagnosticsCount = diagnostics.size()) { @@ -555,17 +555,16 @@ void AbstractMetaBuilderPrivate::traverseDom(const FileModelItem &dom, if (!funcEntry || !funcEntry->generateCode()) continue; - AbstractMetaFunction *metaFunc = traverseFunction(func, nullptr); - if (!metaFunc) + auto metaFuncPtr = traverseFunction(func, nullptr); + if (!metaFuncPtr) continue; - AbstractMetaFunctionCPtr metaFuncPtr(metaFunc); - if (!funcEntry->hasSignature(metaFunc->minimalSignature())) + if (!funcEntry->hasSignature(metaFuncPtr->minimalSignature())) continue; - metaFunc->setTypeEntry(funcEntry); - applyFunctionModifications(metaFunc); - metaFunc->applyTypeModifications(); + metaFuncPtr->setTypeEntry(funcEntry); + applyFunctionModifications(metaFuncPtr); + metaFuncPtr->applyTypeModifications(); setInclude(funcEntry, func->fileName()); @@ -1348,7 +1347,7 @@ void AbstractMetaBuilderPrivate::traverseFields(const ScopeModelItem &scope_item } } -void AbstractMetaBuilderPrivate::fixReturnTypeOfConversionOperator(AbstractMetaFunction *metaFunction) +void AbstractMetaBuilderPrivate::fixReturnTypeOfConversionOperator(const AbstractMetaFunctionPtr &metaFunction) { if (!metaFunction->isConversionOperator()) return; @@ -1372,13 +1371,13 @@ void AbstractMetaBuilderPrivate::fixReturnTypeOfConversionOperator(AbstractMetaF metaFunction->setType(metaType); } -AbstractMetaFunctionRawPtrList +AbstractMetaFunctionList AbstractMetaBuilderPrivate::classFunctionList(const ScopeModelItem &scopeItem, AbstractMetaClass::Attributes *constructorAttributes, const AbstractMetaClassPtr ¤tClass) { *constructorAttributes = {}; - AbstractMetaFunctionRawPtrList result; + AbstractMetaFunctionList result; const FunctionList &scopeFunctionList = scopeItem->functions(); result.reserve(scopeFunctionList.size()); const bool isNamespace = currentClass->isNamespace(); @@ -1388,7 +1387,7 @@ AbstractMetaFunctionRawPtrList } else if (function->isSpaceshipOperator() && !function->isDeleted()) { if (currentClass) AbstractMetaClass::addSynthesizedComparisonOperators(currentClass); - } else if (auto *metaFunction = traverseFunction(function, currentClass)) { + } else if (auto metaFunction = traverseFunction(function, currentClass)) { result.append(metaFunction); } else if (!function->isDeleted() && function->functionType() == CodeModel::Constructor) { auto arguments = function->arguments(); @@ -1404,11 +1403,11 @@ void AbstractMetaBuilderPrivate::traverseFunctions(const ScopeModelItem& scopeIt const AbstractMetaClassPtr &metaClass) { AbstractMetaClass::Attributes constructorAttributes; - const AbstractMetaFunctionRawPtrList functions = + const AbstractMetaFunctionList functions = classFunctionList(scopeItem, &constructorAttributes, metaClass); metaClass->setAttributes(metaClass->attributes() | constructorAttributes); - for (AbstractMetaFunction *metaFunction : functions) { + for (const auto &metaFunction : functions) { if (metaClass->isNamespace()) metaFunction->setCppAttribute(FunctionAttribute::Static); @@ -1461,23 +1460,20 @@ void AbstractMetaBuilderPrivate::traverseFunctions(const ScopeModelItem& scopeIt if (!metaFunction->isDestructor() && !(metaFunction->isPrivate() && metaFunction->functionType() == AbstractMetaFunction::ConstructorFunction)) { - if (metaFunction->isSignal() && metaClass->hasSignal(metaFunction)) - qCWarning(lcShiboken, "%s", qPrintable(msgSignalOverloaded(metaClass, metaFunction))); + if (metaFunction->isSignal() && metaClass->hasSignal(metaFunction.get())) + qCWarning(lcShiboken, "%s", qPrintable(msgSignalOverloaded(metaClass, + metaFunction.get()))); if (metaFunction->isConversionOperator()) fixReturnTypeOfConversionOperator(metaFunction); - AbstractMetaClass::addFunction(metaClass, AbstractMetaFunctionCPtr(metaFunction)); + AbstractMetaClass::addFunction(metaClass, metaFunction); applyFunctionModifications(metaFunction); } else if (metaFunction->isDestructor()) { metaClass->setHasPrivateDestructor(metaFunction->isPrivate()); metaClass->setHasProtectedDestructor(metaFunction->isProtected()); metaClass->setHasVirtualDestructor(metaFunction->isVirtual()); } - if (!metaFunction->ownerClass()) { - delete metaFunction; - metaFunction = nullptr; - } } fillAddedFunctions(metaClass); @@ -1539,7 +1535,7 @@ QStringList AbstractMetaBuilder::definitionNames(const QString &name, return result; } -void AbstractMetaBuilderPrivate::applyFunctionModifications(AbstractMetaFunction *func) +void AbstractMetaBuilderPrivate::applyFunctionModifications(const AbstractMetaFunctionPtr &func) { AbstractMetaFunction& funcRef = *func; for (const FunctionModification &mod : func->modifications(func->implementingClass())) { @@ -1574,7 +1570,8 @@ bool AbstractMetaBuilderPrivate::setupInheritance(const AbstractMetaClassPtr &me &info, &baseContainerType); if (templ) { setupInheritance(templ); - inheritTemplate(metaClass, templ, info); + if (!inheritTemplate(metaClass, templ, info)) + return false; metaClass->typeEntry()->setBaseContainerType(templ->typeEntry()); return true; } @@ -1676,15 +1673,14 @@ static AbstractMetaFunction::FunctionType functionTypeFromName(const QString &); bool AbstractMetaBuilderPrivate::traverseAddedGlobalFunction(const AddedFunctionPtr &addedFunc, QString *errorMessage) { - AbstractMetaFunction *metaFunction = - traverseAddedFunctionHelper(addedFunc, nullptr, errorMessage); + auto metaFunction = traverseAddedFunctionHelper(addedFunc, nullptr, errorMessage); if (metaFunction == nullptr) return false; - m_globalFunctions << AbstractMetaFunctionCPtr(metaFunction); + m_globalFunctions << metaFunction; return true; } -AbstractMetaFunction * +AbstractMetaFunctionPtr AbstractMetaBuilderPrivate::traverseAddedFunctionHelper(const AddedFunctionPtr &addedFunc, const AbstractMetaClassPtr &metaClass /* = {} */, QString *errorMessage) @@ -1695,10 +1691,10 @@ AbstractMetaFunction * msgAddedFunctionInvalidReturnType(addedFunc->name(), addedFunc->returnType().qualifiedName(), *errorMessage, metaClass); - return nullptr; + return {}; } - auto *metaFunction = new AbstractMetaFunction(addedFunc); + auto metaFunction = std::make_shared<AbstractMetaFunction>(addedFunc); metaFunction->setType(returnType.value()); metaFunction->setFunctionType(functionTypeFromName(addedFunc->name())); @@ -1716,8 +1712,7 @@ AbstractMetaFunction * msgAddedFunctionInvalidArgType(addedFunc->name(), arg.typeInfo.qualifiedName(), i + 1, *errorMessage, metaClass); - delete metaFunction; - return nullptr; + return {}; } type->decideUsagePattern(); @@ -1774,8 +1769,7 @@ bool AbstractMetaBuilderPrivate::traverseAddedMemberFunction(const AddedFunction const AbstractMetaClassPtr &metaClass, QString *errorMessage) { - AbstractMetaFunction *metaFunction = - traverseAddedFunctionHelper(addedFunc, metaClass, errorMessage); + auto metaFunction = traverseAddedFunctionHelper(addedFunc, metaClass, errorMessage); if (metaFunction == nullptr) return false; @@ -1795,12 +1789,13 @@ bool AbstractMetaBuilderPrivate::traverseAddedMemberFunction(const AddedFunction metaFunction->setDeclaringClass(metaClass); metaFunction->setImplementingClass(metaClass); - AbstractMetaClass::addFunction(metaClass, AbstractMetaFunctionCPtr(metaFunction)); + AbstractMetaClass::addFunction(metaClass, metaFunction); metaClass->setHasNonPrivateConstructor(true); return true; } -void AbstractMetaBuilderPrivate::fixArgumentNames(AbstractMetaFunction *func, const FunctionModificationList &mods) +void AbstractMetaBuilderPrivate::fixArgumentNames(const AbstractMetaFunctionPtr &func, + const FunctionModificationList &mods) { AbstractMetaArgumentList &arguments = func->arguments(); @@ -1919,7 +1914,7 @@ static AbstractMetaFunction::FunctionType functionTypeFromName(const QString &na // Apply the <array> modifications of the arguments static bool applyArrayArgumentModifications(const FunctionModificationList &functionMods, - AbstractMetaFunction *func, + const AbstractMetaFunctionPtr &func, QString *errorMessage) { for (const FunctionModification &mod : functionMods) { @@ -1980,13 +1975,14 @@ void AbstractMetaBuilderPrivate::rejectFunction(const FunctionModelItem &functio m_rejectedFunctions.insert({reason, signatureWithType, sortKey, rejectReason}); } -AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const FunctionModelItem &functionItem, - const AbstractMetaClassPtr ¤tClass) +AbstractMetaFunctionPtr + AbstractMetaBuilderPrivate::traverseFunction(const FunctionModelItem &functionItem, + const AbstractMetaClassPtr ¤tClass) { const auto *tdb = TypeDatabase::instance(); if (!functionItem->templateParameters().isEmpty()) - return nullptr; + return {}; if (functionItem->isDeleted()) { switch (functionItem->functionType()) { @@ -2000,7 +1996,7 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const Functio default: break; } - return nullptr; + return {}; } const QString &functionName = functionItem->name(); const QString className = currentClass != nullptr ? @@ -2010,7 +2006,7 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const Functio // Skip enum helpers generated by Q_ENUM if ((currentClass == nullptr || currentClass->isNamespace()) && (functionName == u"qt_getEnumMetaObject" || functionName == u"qt_getEnumName")) { - return nullptr; + return {}; } // Clang: Skip qt_metacast(), qt_metacall(), expanded from Q_OBJECT @@ -2018,10 +2014,10 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const Functio if (currentClass != nullptr) { if (functionName == u"qt_check_for_QGADGET_macro" || functionName.startsWith(u"qt_meta")) { - return nullptr; + return {}; } if (functionName == u"metaObject" && className != u"QObject") - return nullptr; + return {}; } } // PySide extensions @@ -2029,7 +2025,7 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const Functio if (tdb->isFunctionRejected(className, functionName, &rejectReason)) { rejectFunction(functionItem, currentClass, AbstractMetaBuilder::GenerationDisabled, rejectReason); - return nullptr; + return {}; } const QString &signature = functionSignature(functionItem); @@ -2040,22 +2036,22 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const Functio qCInfo(lcShiboken, "%s::%s was rejected by the type database (%s).", qPrintable(className), qPrintable(signature), qPrintable(rejectReason)); } - return nullptr; + return {}; } if (functionItem->isFriend()) - return nullptr; + return {}; const auto cppAttributes = functionItem->attributes(); const bool deprecated = cppAttributes.testFlag(FunctionAttribute::Deprecated); if (deprecated && m_skipDeprecated) { rejectFunction(functionItem, currentClass, AbstractMetaBuilder::GenerationDisabled, u" is deprecated."_s); - return nullptr; + return {}; } AbstractMetaFunction::Flags flags; - auto *metaFunction = new AbstractMetaFunction(functionName); + auto metaFunction = std::make_shared<AbstractMetaFunction>(functionName); metaFunction->setCppAttributes(cppAttributes); const QByteArray cSignature = signature.toUtf8(); const QString unresolvedSignature = @@ -2088,8 +2084,7 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const Functio if (tdb->isReturnTypeRejected(className, returnType.toString(), &rejectReason)) { rejectFunction(functionItem, currentClass, AbstractMetaBuilder::GenerationDisabled, rejectReason); - delete metaFunction; - return nullptr; + return {}; } TranslateTypeFlags flags; @@ -2103,8 +2098,7 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const Functio qPrintable(msgSkippingFunction(functionItem, signature, reason))); rejectFunction(functionItem, currentClass, AbstractMetaBuilder::UnmatchedReturnType, reason); - delete metaFunction; - return nullptr; + return {}; } metaFunction->setType(type.value()); @@ -2135,8 +2129,7 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const Functio if (tdb->isArgumentTypeRejected(className, arg->type().toString(), &rejectReason)) { rejectFunction(functionItem, currentClass, AbstractMetaBuilder::GenerationDisabled, rejectReason); - delete metaFunction; - return nullptr; + return {}; } TranslateTypeFlags flags; @@ -2163,8 +2156,7 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const Functio qPrintable(msgSkippingFunction(functionItem, signature, reason))); rejectFunction(functionItem, currentClass, AbstractMetaBuilder::UnmatchedArgumentType, reason); - delete metaFunction; - return nullptr; + return {}; } auto metaType = metaTypeO.value(); @@ -2187,8 +2179,8 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const Functio AbstractMetaArgumentList &metaArguments = metaFunction->arguments(); const FunctionModificationList functionMods = currentClass - ? AbstractMetaFunction::findClassModifications(metaFunction, currentClass) - : AbstractMetaFunction::findGlobalModifications(metaFunction); + ? AbstractMetaFunction::findClassModifications(metaFunction.get(), currentClass) + : AbstractMetaFunction::findGlobalModifications(metaFunction.get()); applyCachedFunctionModifications(metaFunction, functionMods); @@ -2213,7 +2205,7 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const Functio && metaFunction->argumentName(i + 1, false, currentClass).isEmpty()) { qCWarning(lcShiboken, "%s", qPrintable(msgUnnamedArgumentDefaultExpression(currentClass, i + 1, - className, metaFunction))); + className, metaFunction.get()))); } } @@ -3128,80 +3120,103 @@ std::optional<AbstractMetaType> AbstractMetaClassPtr AbstractMetaBuilder::inheritTemplateClass(const ComplexTypeEntryPtr &te, const AbstractMetaClassCPtr &templateClass, - const AbstractMetaTypeList &templateTypes, - InheritTemplateFlags flags) + const AbstractMetaTypeList &templateTypes) { auto result = std::make_shared<AbstractMetaClass>(); result->setTypeDef(true); result->setTypeEntry(te); if (!AbstractMetaBuilderPrivate::inheritTemplate(result, templateClass, - templateTypes, flags)) { + templateTypes)) { return {}; } AbstractMetaBuilderPrivate::inheritTemplateFunctions(result); return result; } + +static std::optional<AbstractMetaType> + inheritTemplateParameter(const AbstractMetaClassPtr &subclass, + const AbstractMetaClassCPtr &templateClass, + const TypeInfo &info, QString *errorMessage) +{ + QString typeName = info.qualifiedName().join("::"_L1); + TypeDatabase *typeDb = TypeDatabase::instance(); + TypeEntryPtr t; + // 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. + if (isNumber(typeName)) { + t = typeDb->findType(typeName); + if (!t) { + auto parent = typeSystemTypeEntry(subclass->typeEntry()); + t = TypeDatabase::instance()->addConstantValueTypeEntry(typeName, parent); + } + } else { + QStringList possibleNames; + possibleNames << subclass->qualifiedCppName() + "::"_L1 + typeName; + possibleNames << templateClass->qualifiedCppName() + "::"_L1 + typeName; + if (subclass->enclosingClass()) + possibleNames << subclass->enclosingClass()->qualifiedCppName() + "::"_L1 + typeName; + possibleNames << typeName; + + for (const QString &possibleName : std::as_const(possibleNames)) { + t = typeDb->findType(possibleName); + if (t) + break; + } + } + + if (!t) { + *errorMessage = msgIgnoringTemplateParameter(typeName, + "The corresponding type was not found in the typesystem."); + return std::nullopt; + } + + if (t->isContainer()) { + *errorMessage = msgIgnoringTemplateParameter(typeName, + "Template inheritance from nested containers is not supported"); + return std::nullopt; + } + AbstractMetaType result(t); + result.setConstant(info.isConstant()); + result.setReferenceType(info.referenceType()); + result.setIndirectionsV(info.indirectionsV()); + result.decideUsagePattern(); + return result; +} + bool AbstractMetaBuilderPrivate::inheritTemplate(const AbstractMetaClassPtr &subclass, const AbstractMetaClassCPtr &templateClass, const TypeInfo &info) { AbstractMetaTypeList templateTypes; + QString errorMessage; for (const TypeInfo &i : info.instantiations()) { - QString typeName = i.qualifiedName().join(u"::"_s); - TypeDatabase *typeDb = TypeDatabase::instance(); - TypeEntryPtr t; - // 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. - if (isNumber(typeName)) { - t = typeDb->findType(typeName); - if (!t) { - auto parent = typeSystemTypeEntry(subclass->typeEntry()); - t = TypeDatabase::instance()->addConstantValueTypeEntry(typeName, parent); - } - } else { - QStringList possibleNames; - possibleNames << subclass->qualifiedCppName() + u"::"_s + typeName; - possibleNames << templateClass->qualifiedCppName() + u"::"_s + typeName; - if (subclass->enclosingClass()) - possibleNames << subclass->enclosingClass()->qualifiedCppName() + u"::"_s + typeName; - possibleNames << typeName; - - for (const QString &possibleName : std::as_const(possibleNames)) { - t = typeDb->findType(possibleName); - if (t) - break; - } - } - - if (t) { - AbstractMetaType temporaryType(t); - temporaryType.setConstant(i.isConstant()); - temporaryType.setReferenceType(i.referenceType()); - temporaryType.setIndirectionsV(i.indirectionsV()); - temporaryType.decideUsagePattern(); - templateTypes << temporaryType; + const auto typeO = inheritTemplateParameter(subclass, templateClass, i, &errorMessage); + if (typeO.has_value()) { + templateTypes.append(typeO.value()); } else { - qCWarning(lcShiboken).noquote().nospace() - << "Ignoring template parameter " << typeName << " from " - << info.toString() << ". The corresponding type was not found in the typesystem."; + errorMessage = msgInheritTemplateIssue(subclass, info, errorMessage); + qCWarning(lcShiboken, "%s", qPrintable(errorMessage)); } } + if (templateTypes.isEmpty()) { + errorMessage = msgInheritTemplateIssue(subclass, info, + "No template parameters could be inherited"_L1); + qCWarning(lcShiboken, "%s", qPrintable(errorMessage)); + return false; + } return inheritTemplate(subclass, templateClass, templateTypes); } bool AbstractMetaBuilderPrivate::inheritTemplate(const AbstractMetaClassPtr &subclass, const AbstractMetaClassCPtr &templateClass, - const AbstractMetaTypeList &templateTypes, - InheritTemplateFlags flags) + const AbstractMetaTypeList &templateTypes) { subclass->setTemplateBaseClass(templateClass); - if (flags.testFlag(InheritTemplateFlag::SetEnclosingClass)) - subclass->setEnclosingClass(templateClass->enclosingClass()); subclass->setTemplateBaseClassInstantiations(templateTypes); subclass->setBaseClass(templateClass->baseClass()); return true; @@ -3291,7 +3306,7 @@ AbstractMetaFunctionPtr } QString errorMessage; - if (!applyArrayArgumentModifications(f->modifications(subclass), f.get(), + if (!applyArrayArgumentModifications(f->modifications(subclass), f, &errorMessage)) { qCWarning(lcShiboken, "While specializing %s (%s): %s", qPrintable(subclass->name()), qPrintable(templateClass->name()), diff --git a/sources/shiboken6/ApiExtractor/abstractmetabuilder.h b/sources/shiboken6/ApiExtractor/abstractmetabuilder.h index cbd8c7034..20261ed3c 100644 --- a/sources/shiboken6/ApiExtractor/abstractmetabuilder.h +++ b/sources/shiboken6/ApiExtractor/abstractmetabuilder.h @@ -100,10 +100,9 @@ public: const AbstractMetaTypeList &templateTypes); static AbstractMetaClassPtr - inheritTemplateClass(const ComplexTypeEntryPtr &te, - const AbstractMetaClassCPtr &templateClass, - const AbstractMetaTypeList &templateTypes, - InheritTemplateFlags flags = {}); + inheritTemplateClass(const ComplexTypeEntryPtr &te, + const AbstractMetaClassCPtr &templateClass, + const AbstractMetaTypeList &templateTypes); /// Performs a template specialization of the member function. /// \param function Member function diff --git a/sources/shiboken6/ApiExtractor/abstractmetabuilder_p.h b/sources/shiboken6/ApiExtractor/abstractmetabuilder_p.h index d7aaba5b0..4e337339e 100644 --- a/sources/shiboken6/ApiExtractor/abstractmetabuilder_p.h +++ b/sources/shiboken6/ApiExtractor/abstractmetabuilder_p.h @@ -98,20 +98,21 @@ public: const QSet<QString> &enumsDeclarations); void traverseEnums(const ScopeModelItem &item, const AbstractMetaClassPtr &parent, const QStringList &enumsDeclarations); - AbstractMetaFunctionRawPtrList classFunctionList(const ScopeModelItem &scopeItem, - AbstractMetaClass::Attributes *constructorAttributes, - const AbstractMetaClassPtr ¤tClass); + AbstractMetaFunctionList classFunctionList(const ScopeModelItem &scopeItem, + AbstractMetaClass::Attributes *constructorAttributes, + const AbstractMetaClassPtr ¤tClass); void traverseFunctions(const ScopeModelItem& item, const AbstractMetaClassPtr &parent); - static void applyFunctionModifications(AbstractMetaFunction *func); + static void applyFunctionModifications(const AbstractMetaFunctionPtr &func); void traverseFields(const ScopeModelItem &item, const AbstractMetaClassPtr &parent); bool traverseStreamOperator(const FunctionModelItem &functionItem, const AbstractMetaClassPtr ¤tClass); void traverseOperatorFunction(const FunctionModelItem &item, const AbstractMetaClassPtr ¤tClass); - AbstractMetaFunction *traverseAddedFunctionHelper(const AddedFunctionPtr &addedFunc, - const AbstractMetaClassPtr &metaClass, - QString *errorMessage); + AbstractMetaFunctionPtr + traverseAddedFunctionHelper(const AddedFunctionPtr &addedFunc, + const AbstractMetaClassPtr &metaClass, + QString *errorMessage); bool traverseAddedGlobalFunction(const AddedFunctionPtr &addedFunc, QString *errorMessage); bool traverseAddedMemberFunction(const AddedFunctionPtr &addedFunc, @@ -121,8 +122,9 @@ public: const AbstractMetaClassPtr ¤tClass, AbstractMetaBuilder::RejectReason reason, const QString &rejectReason); - AbstractMetaFunction *traverseFunction(const FunctionModelItem &function, - const AbstractMetaClassPtr ¤tClass); + AbstractMetaFunctionPtr + traverseFunction(const FunctionModelItem &function, + const AbstractMetaClassPtr ¤tClass); std::optional<AbstractMetaField> traverseField(const VariableModelItem &field, const AbstractMetaClassCPtr &cls); void checkFunctionModifications() const; @@ -142,7 +144,7 @@ public: * said class. * \param metaFunction conversion operator function to be fixed. */ - static void fixReturnTypeOfConversionOperator(AbstractMetaFunction *metaFunction); + static void fixReturnTypeOfConversionOperator(const AbstractMetaFunctionPtr &metaFunction); void parseQ_Properties(const AbstractMetaClassPtr &metaClass, const QStringList &declarations); @@ -190,8 +192,7 @@ public: const TypeInfo &info); static bool inheritTemplate(const AbstractMetaClassPtr &subclass, const AbstractMetaClassCPtr &templateClass, - const AbstractMetaTypeList &templateTypes, - InheritTemplateFlags flags = {}); + const AbstractMetaTypeList &templateTypes); static AbstractMetaFunctionPtr inheritTemplateFunction(const AbstractMetaFunctionCPtr &function, @@ -213,7 +214,8 @@ public: void sortLists(); void setInclude(const TypeEntryPtr &te, const QString &path) const; - static void fixArgumentNames(AbstractMetaFunction *func, const FunctionModificationList &mods); + static void fixArgumentNames(const AbstractMetaFunctionPtr &func, + const FunctionModificationList &mods); void fillAddedFunctions(const AbstractMetaClassPtr &metaClass); AbstractMetaClassCPtr resolveTypeSystemTypeDef(const AbstractMetaType &t) const; diff --git a/sources/shiboken6/ApiExtractor/abstractmetalang_typedefs.h b/sources/shiboken6/ApiExtractor/abstractmetalang_typedefs.h index 802f549cf..27321ca2d 100644 --- a/sources/shiboken6/ApiExtractor/abstractmetalang_typedefs.h +++ b/sources/shiboken6/ApiExtractor/abstractmetalang_typedefs.h @@ -30,6 +30,7 @@ using AbstractMetaEnumValueList = QList<AbstractMetaEnumValue>; using AbstractMetaFieldList = QList<AbstractMetaField>; using AbstractMetaFunctionRawPtrList = QList<AbstractMetaFunction *>; using AbstractMetaFunctionCList = QList<AbstractMetaFunctionCPtr>; +using AbstractMetaFunctionList = QList<AbstractMetaFunctionPtr>; using AbstractMetaTypeList = QList<AbstractMetaType>; using UsingMembers = QList<UsingMember>; diff --git a/sources/shiboken6/ApiExtractor/abstractmetatype.cpp b/sources/shiboken6/ApiExtractor/abstractmetatype.cpp index dcfc74bbb..3ec07509d 100644 --- a/sources/shiboken6/ApiExtractor/abstractmetatype.cpp +++ b/sources/shiboken6/ApiExtractor/abstractmetatype.cpp @@ -543,6 +543,7 @@ void AbstractMetaType::decideUsagePattern() pattern = ObjectPattern; } setTypeUsagePattern(pattern); + Q_ASSERT(pattern != ContainerPattern || !d->m_instantiations.isEmpty()); } bool AbstractMetaTypeData::hasTemplateChildren() const diff --git a/sources/shiboken6/ApiExtractor/apiextractor.cpp b/sources/shiboken6/ApiExtractor/apiextractor.cpp index 83ee4437e..786cd0783 100644 --- a/sources/shiboken6/ApiExtractor/apiextractor.cpp +++ b/sources/shiboken6/ApiExtractor/apiextractor.cpp @@ -456,11 +456,10 @@ AbstractMetaFunctionPtr AbstractMetaClassPtr ApiExtractor::inheritTemplateClass(const ComplexTypeEntryPtr &te, const AbstractMetaClassCPtr &templateClass, - const AbstractMetaTypeList &templateTypes, - InheritTemplateFlags flags) + const AbstractMetaTypeList &templateTypes) { return AbstractMetaBuilder::inheritTemplateClass(te, templateClass, - templateTypes, flags); + templateTypes); } QString ApiExtractorPrivate::getSimplifiedContainerTypeName(const AbstractMetaType &type) @@ -606,7 +605,15 @@ void ApiExtractorPrivate::addInstantiatedSmartPointer(InstantiationCollectContex const auto ste = std::static_pointer_cast<const SmartPointerTypeEntry>(smp.smartPointer->typeEntry()); QString name = ste->getTargetName(smp.type); auto parentTypeEntry = ste->parent(); - InheritTemplateFlags flags; + + // FIXME PYSIDE 7: Make global scope the default behavior? + // Note: Also requires changing SmartPointerTypeEntry::getTargetName() + // to not strip namespaces from unnamed instances. + const bool globalScope = name.startsWith("::"_L1); + if (globalScope) { + name.remove(0, 2); + parentTypeEntry = typeSystemTypeEntry(ste); + } auto colonPos = name.lastIndexOf(u"::"); const bool withinNameSpace = colonPos != -1; @@ -617,19 +624,18 @@ void ApiExtractorPrivate::addInstantiatedSmartPointer(InstantiationCollectContex if (nameSpaces.isEmpty()) throw Exception(msgNamespaceNotFound(name)); parentTypeEntry = nameSpaces.constFirst(); - } else { - flags.setFlag(InheritTemplateFlag::SetEnclosingClass); } TypedefEntryPtr typedefEntry(new TypedefEntry(name, ste->name(), ste->version(), parentTypeEntry)); typedefEntry->setTargetLangPackage(ste->targetLangPackage()); auto instantiationEntry = TypeDatabase::initializeTypeDefEntry(typedefEntry, ste); + instantiationEntry->setParent(parentTypeEntry); smp.specialized = ApiExtractor::inheritTemplateClass(instantiationEntry, smp.smartPointer, - {instantiatedType}, flags); + {instantiatedType}); Q_ASSERT(smp.specialized); - if (withinNameSpace) { // move class to desired namespace + if (parentTypeEntry->type() != TypeEntry::TypeSystemType) { // move class to desired namespace const auto enclClass = AbstractMetaClass::findClass(m_builder->classes(), parentTypeEntry); Q_ASSERT(enclClass); auto specialized = std::const_pointer_cast<AbstractMetaClass>(smp.specialized); diff --git a/sources/shiboken6/ApiExtractor/apiextractor.h b/sources/shiboken6/ApiExtractor/apiextractor.h index feae9454c..7bff5c252 100644 --- a/sources/shiboken6/ApiExtractor/apiextractor.h +++ b/sources/shiboken6/ApiExtractor/apiextractor.h @@ -73,8 +73,7 @@ public: static AbstractMetaClassPtr inheritTemplateClass(const ComplexTypeEntryPtr &te, const AbstractMetaClassCPtr &templateClass, - const AbstractMetaTypeList &templateTypes, - InheritTemplateFlags flags = {}); + const AbstractMetaTypeList &templateTypes); private: ApiExtractorPrivate *d; diff --git a/sources/shiboken6/ApiExtractor/apiextractorflags.h b/sources/shiboken6/ApiExtractor/apiextractorflags.h index 4fe6ecc1a..6f69b8b77 100644 --- a/sources/shiboken6/ApiExtractor/apiextractorflags.h +++ b/sources/shiboken6/ApiExtractor/apiextractorflags.h @@ -15,12 +15,4 @@ enum class ApiExtractorFlag Q_DECLARE_FLAGS(ApiExtractorFlags, ApiExtractorFlag) Q_DECLARE_OPERATORS_FOR_FLAGS(ApiExtractorFlags) -enum class InheritTemplateFlag -{ - SetEnclosingClass = 0x1 -}; - -Q_DECLARE_FLAGS(InheritTemplateFlags, InheritTemplateFlag) -Q_DECLARE_OPERATORS_FOR_FLAGS(InheritTemplateFlags) - #endif // APIEXTRACTORFLAGS_H diff --git a/sources/shiboken6/ApiExtractor/apiextractorresult.cpp b/sources/shiboken6/ApiExtractor/apiextractorresult.cpp index 2a48a30d1..5a433bbeb 100644 --- a/sources/shiboken6/ApiExtractor/apiextractorresult.cpp +++ b/sources/shiboken6/ApiExtractor/apiextractorresult.cpp @@ -7,6 +7,7 @@ #include "enumtypeentry.h" #include "flagstypeentry.h" +#include "smartpointertypeentry.h" ApiExtractorResult::ApiExtractorResult() = default; @@ -50,6 +51,18 @@ const InstantiatedSmartPointers &ApiExtractorResult::instantiatedSmartPointers() return m_instantiatedSmartPointers; } +std::optional<InstantiatedSmartPointer> + ApiExtractorResult::findSmartPointerInstantiation(const SmartPointerTypeEntryCPtr &pointer, + const TypeEntryCPtr &pointee) const +{ + for (const auto &smp : m_instantiatedSmartPointers) { + const auto &i = smp.type; + if (i.typeEntry() == pointer && i.instantiations().at(0).typeEntry() == pointee) + return smp; + } + return std::nullopt; +} + const QMultiHash<QString, QString> &ApiExtractorResult::typedefTargetToName() const { return m_typedefTargetToName; diff --git a/sources/shiboken6/ApiExtractor/apiextractorresult.h b/sources/shiboken6/ApiExtractor/apiextractorresult.h index 88a2093f1..b2aae88ed 100644 --- a/sources/shiboken6/ApiExtractor/apiextractorresult.h +++ b/sources/shiboken6/ApiExtractor/apiextractorresult.h @@ -43,6 +43,9 @@ public: const AbstractMetaTypeList &instantiatedContainers() const; const InstantiatedSmartPointers &instantiatedSmartPointers() const; + std::optional<InstantiatedSmartPointer> + findSmartPointerInstantiation(const SmartPointerTypeEntryCPtr &pointer, + const TypeEntryCPtr &pointee) const; const QMultiHash<QString, QString> &typedefTargetToName() const; diff --git a/sources/shiboken6/ApiExtractor/clangparser/clangparser.cpp b/sources/shiboken6/ApiExtractor/clangparser/clangparser.cpp index da6930476..6c0cf3ae2 100644 --- a/sources/shiboken6/ApiExtractor/clangparser/clangparser.cpp +++ b/sources/shiboken6/ApiExtractor/clangparser/clangparser.cpp @@ -235,6 +235,7 @@ static QByteArray msgCreateTranslationUnit(const QByteArrayList &clangArgs, unsi static CXTranslationUnit createTranslationUnit(CXIndex index, const QByteArrayList &args, bool addCompilerSupportArguments, + LanguageLevel level, unsigned flags = 0) { // courtesy qdoc @@ -255,7 +256,7 @@ static CXTranslationUnit createTranslationUnit(CXIndex index, QByteArrayList clangArgs; if (addCompilerSupportArguments) { - clangArgs += emulatedCompilerOptions(); + clangArgs += emulatedCompilerOptions(level); clangArgs += defaultArgs; } clangArgs += detectVulkan(); @@ -280,7 +281,7 @@ static CXTranslationUnit createTranslationUnit(CXIndex index, */ bool parse(const QByteArrayList &clangArgs, bool addCompilerSupportArguments, - unsigned clangFlags, BaseVisitor &bv) + LanguageLevel level, unsigned clangFlags, BaseVisitor &bv) { CXIndex index = clang_createIndex(0 /* excludeDeclarationsFromPCH */, 1 /* displayDiagnostics */); @@ -291,7 +292,7 @@ bool parse(const QByteArrayList &clangArgs, bool addCompilerSupportArguments, CXTranslationUnit translationUnit = createTranslationUnit(index, clangArgs, addCompilerSupportArguments, - clangFlags); + level, clangFlags); if (!translationUnit) return false; diff --git a/sources/shiboken6/ApiExtractor/clangparser/clangparser.h b/sources/shiboken6/ApiExtractor/clangparser/clangparser.h index 4a46248e4..22e0a50cd 100644 --- a/sources/shiboken6/ApiExtractor/clangparser/clangparser.h +++ b/sources/shiboken6/ApiExtractor/clangparser/clangparser.h @@ -14,6 +14,8 @@ #include <string_view> #include <utility> +enum class LanguageLevel; + namespace clang { struct Diagnostic; @@ -79,7 +81,7 @@ private: bool parse(const QByteArrayList &clangArgs, bool addCompilerSupportArguments, - unsigned clangFlags, BaseVisitor &ctx); + LanguageLevel level, unsigned clangFlags, BaseVisitor &ctx); } // namespace clang diff --git a/sources/shiboken6/ApiExtractor/clangparser/compilersupport.cpp b/sources/shiboken6/ApiExtractor/clangparser/compilersupport.cpp index 4c13b141f..20224020b 100644 --- a/sources/shiboken6/ApiExtractor/clangparser/compilersupport.cpp +++ b/sources/shiboken6/ApiExtractor/clangparser/compilersupport.cpp @@ -106,7 +106,7 @@ static bool runProcess(const QString &program, const QStringList &arguments, QProcess process; process.start(program, arguments, QProcess::ReadWrite); if (!process.waitForStarted()) { - qWarning().noquote().nospace() << "Unable to start " + qCWarning(lcShiboken).noquote().nospace() << "Unable to start " << process.program() << ": " << process.errorString(); return false; } @@ -119,18 +119,18 @@ static bool runProcess(const QString &program, const QStringList &arguments, *stdOutIn = process.readAllStandardOutput(); if (!finished) { - qWarning().noquote().nospace() << process.program() << " timed out: " << stdErr; + qCWarning(lcShiboken).noquote().nospace() << process.program() << " timed out: " << stdErr; process.kill(); return false; } if (process.exitStatus() != QProcess::NormalExit) { - qWarning().noquote().nospace() << process.program() << " crashed: " << stdErr; + qCWarning(lcShiboken).noquote().nospace() << process.program() << " crashed: " << stdErr; return false; } if (process.exitCode() != 0) { - qWarning().noquote().nospace() << process.program() << " exited " + qCWarning(lcShiboken).noquote().nospace() << process.program() << " exited " << process.exitCode() << ": " << stdErr; return false; } @@ -263,8 +263,8 @@ static QString queryLlvmConfigDir(const QString &arg) return {}; const QString path = QFile::decodeName(stdOut.trimmed()); if (!QFileInfo::exists(path)) { - qWarning(R"(%s: "%s" as returned by llvm-config "%s" does not exist.)", - __FUNCTION__, qPrintable(QDir::toNativeSeparators(path)), qPrintable(arg)); + qCWarning(lcShiboken, R"(%s: "%s" as returned by llvm-config "%s" does not exist.)", + __FUNCTION__, qPrintable(QDir::toNativeSeparators(path)), qPrintable(arg)); return {}; } return path; @@ -277,7 +277,8 @@ static QString findClangLibDir() const QString path = QFile::decodeName(qgetenv(envVar)) + u"/lib"_s; if (QFileInfo::exists(path)) return path; - qWarning("%s: %s as pointed to by %s does not exist.", __FUNCTION__, qPrintable(path), envVar); + qCWarning(lcShiboken, "%s: %s as pointed to by %s does not exist.", + __FUNCTION__, qPrintable(path), envVar); } } return queryLlvmConfigDir(u"--libdir"_s); @@ -289,13 +290,23 @@ static QString findClangBuiltInIncludesDir() const QString clangPathLibDir = findClangLibDir(); if (!clangPathLibDir.isEmpty()) { QString candidate; + QString clangDirName = clangPathLibDir + u"/clang"_s; + // PYSIDE-2769: llvm-config --libdir may report /usr/lib64 on manylinux_2_28_x86_64 + // whereas the includes are under /usr/lib/clang/../include. + if (!QFileInfo::exists(clangDirName) && clangPathLibDir.endsWith("64"_L1)) { + const QString fallback = clangPathLibDir.sliced(0, clangPathLibDir.size() - 2); + clangDirName = fallback + u"/clang"_s; + qCWarning(lcShiboken, "%s: Falling back from %s to %s.", + __FUNCTION__, qPrintable(clangPathLibDir), qPrintable(fallback)); + } + QVersionNumber lastVersionNumber(1, 0, 0); - const QString clangDirName = clangPathLibDir + u"/clang"_s; QDir clangDir(clangDirName); const QFileInfoList versionDirs = clangDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); if (versionDirs.isEmpty()) - qWarning("%s: No subdirectories found in %s.", __FUNCTION__, qPrintable(clangDirName)); + qCWarning(lcShiboken, "%s: No subdirectories found in %s.", + __FUNCTION__, qPrintable(clangDirName)); for (const QFileInfo &fi : versionDirs) { const QString fileName = fi.fileName(); if (fileName.at(0).isDigit()) { @@ -365,14 +376,15 @@ static void appendClangBuiltinIncludes(HeaderPaths *p) } // Returns clang options needed for emulating the host compiler -QByteArrayList emulatedCompilerOptions() +QByteArrayList emulatedCompilerOptions(LanguageLevel level) { QByteArrayList result; HeaderPaths headerPaths; switch (compiler()) { case Compiler::Msvc: result.append("-fms-compatibility-version="_ba + msvcCompatVersion()); - result.append(QByteArrayLiteral("-fdelayed-template-parsing")); + if (level < LanguageLevel::Cpp20) + result.append("-fdelayed-template-parsing"_ba); result.append(QByteArrayLiteral("-Wno-microsoft-enum-value")); result.append("/Zc:__cplusplus"_ba); // Fix yvals_core.h: STL1000: Unexpected compiler version, expected Clang 7 or newer (MSVC2017 update) diff --git a/sources/shiboken6/ApiExtractor/clangparser/compilersupport.h b/sources/shiboken6/ApiExtractor/clangparser/compilersupport.h index 462e8f205..f1d63b7c3 100644 --- a/sources/shiboken6/ApiExtractor/clangparser/compilersupport.h +++ b/sources/shiboken6/ApiExtractor/clangparser/compilersupport.h @@ -33,7 +33,7 @@ enum class Platform { namespace clang { QVersionNumber libClangVersion(); -QByteArrayList emulatedCompilerOptions(); +QByteArrayList emulatedCompilerOptions(LanguageLevel level); LanguageLevel emulatedCompilerLanguageLevel(); const char *languageLevelOption(LanguageLevel l); diff --git a/sources/shiboken6/ApiExtractor/messages.cpp b/sources/shiboken6/ApiExtractor/messages.cpp index f9f46f520..170595660 100644 --- a/sources/shiboken6/ApiExtractor/messages.cpp +++ b/sources/shiboken6/ApiExtractor/messages.cpp @@ -481,6 +481,21 @@ QString msgCannotFindTypeEntryForSmartPointer(const QString &t, const QString &s + u"\" for instantiation of \""_s +smartPointerType + u"\"."_s; } +QString msgInheritTemplateIssue(const AbstractMetaClassPtr &subclass, + const TypeInfo &info, + const QString &what) +{ + return "While inheriting template "_L1 + subclass->name() + + " from "_L1 + info.toString() + ": "_L1 + what; +} + +QString msgIgnoringTemplateParameter(const QString &typeName, + const char *why) +{ + return "Ignoring template parameter "_L1 + typeName + + ": "_L1 + QLatin1StringView(why); +} + QString msgInvalidSmartPointerType(const TypeInfo &i) { return u"Invalid smart pointer type \""_s +i.toString() + u"\"."_s; @@ -980,3 +995,10 @@ QString msgInvalidLanguageLevel(const QString &l) { return u"Invalid argument for language level: \""_s + l + u"\"."_s; } + +QString msgCannotFindImage(const QString &href, const QString &context, + const QString &candidate) +{ + return "Cannot resolve image "_L1 + href + " for "_L1 + context + + " (tried "_L1 + QDir::toNativeSeparators(candidate) + ")."_L1; +} diff --git a/sources/shiboken6/ApiExtractor/messages.h b/sources/shiboken6/ApiExtractor/messages.h index 2899cbdfa..5216b26a7 100644 --- a/sources/shiboken6/ApiExtractor/messages.h +++ b/sources/shiboken6/ApiExtractor/messages.h @@ -126,6 +126,10 @@ QString msgUnableToTranslateType(const TypeInfo &typeInfo, QString msgCannotFindTypeEntry(const QString &t); QString msgCannotFindTypeEntryForSmartPointer(const QString &t, const QString &smartPointerType); +QString msgInheritTemplateIssue(const AbstractMetaClassPtr &subclass, + const TypeInfo &info, const QString &what); +QString msgIgnoringTemplateParameter(const QString &typeName, + const char *why); QString msgInvalidSmartPointerType(const TypeInfo &i); QString msgCannotFindSmartPointerInstantion(const TypeInfo &i); @@ -257,4 +261,7 @@ QString msgMissingProjectFileMarker(const QString &name, const QByteArray &start QString msgInvalidLanguageLevel(const QString &l); +QString msgCannotFindImage(const QString &href, const QString &context, + const QString &candidate); + #endif // MESSAGES_H diff --git a/sources/shiboken6/ApiExtractor/typedatabase.cpp b/sources/shiboken6/ApiExtractor/typedatabase.cpp index 749c4baa3..61fd22418 100644 --- a/sources/shiboken6/ApiExtractor/typedatabase.cpp +++ b/sources/shiboken6/ApiExtractor/typedatabase.cpp @@ -88,7 +88,8 @@ static const PythonTypes &builtinPythonTypes() {u"PyObject"_s, u"true"_s, TypeSystem::CPythonType::Other}, // shiboken-specific {u"PyPathLike"_s, u"Shiboken::String::checkPath"_s, TypeSystem::CPythonType::Other}, - {u"PySequence"_s, u"Shiboken::String::checkIterable"_s, TypeSystem::CPythonType::Other}, + {u"PySequence"_s, u"Shiboken::String::checkIterableArgument"_s, + TypeSystem::CPythonType::Other}, {u"PyUnicode"_s, u"PyUnicode_Check"_s, TypeSystem::CPythonType::String}, {u"PyTypeObject"_s, u"PyType_Check"_s, TypeSystem::CPythonType::Other}, {u"str"_s, u"Shiboken::String::check"_s, TypeSystem::CPythonType::String}, diff --git a/sources/shiboken6/cmake/ShibokenHelpers.cmake b/sources/shiboken6/cmake/ShibokenHelpers.cmake index 8bc066102..cff6df95e 100644 --- a/sources/shiboken6/cmake/ShibokenHelpers.cmake +++ b/sources/shiboken6/cmake/ShibokenHelpers.cmake @@ -202,12 +202,6 @@ macro(get_python_extension_suffix) # Python_SOABI is only set by CMake 3.17+ # TODO: Lower this to CMake 3.16 if possible. if(SHIBOKEN_IS_CROSS_BUILD) - # For android platform armv7a FindPython module return Python_SOABI as empty because - # it is unable to set Python_CONFIG i.e. find `python3-config` script - # This workaround sets the Python_SOABI manually for this platform. - if(CMAKE_SYSTEM_NAME STREQUAL "Android" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "armv7-a") - set(Python_SOABI "cpython-311}") - endif() if(NOT Python_SOABI) message(FATAL_ERROR "Python_SOABI variable is empty.") endif() @@ -320,6 +314,17 @@ macro(shiboken_find_required_python) "${_shiboken_backup_CMAKE_FIND_ROOT_PATH_MODE_PROGRAM}") set(CMAKE_FIND_ROOT_PATH "${_shiboken_backup_CMAKE_FIND_ROOT_PATH}") + + # For Android platform sometimes the FindPython module returns Python_SOABI as empty in + # certain scenarios eg: armv7a target, macOS host etc. This is because + # it is unable to set Python_CONFIG i.e. `python3-config` script + # This workaround sets the Python_SOABI manually for this Android platform. + # This needs to be updated manually if the Python version for Android cross compilation + # changes. + # TODO: Find a better way to set Python_SOABI for Android platform + if(CMAKE_SYSTEM_NAME STREQUAL "Android" AND NOT Python_SOABI) + set(Python_SOABI "cpython-311") + endif() else() find_package( Python diff --git a/sources/shiboken6/doc/shibokenmodule.rst b/sources/shiboken6/doc/shibokenmodule.rst index 2f1c6d166..3bc4fa6ba 100644 --- a/sources/shiboken6/doc/shibokenmodule.rst +++ b/sources/shiboken6/doc/shibokenmodule.rst @@ -125,6 +125,11 @@ To import the module: Dumps the map of wrappers existing in libshiboken to standard error. +.. function:: dumpConverters() + + Dumps the map of named converters existing in libshiboken to standard + error. + .. py:class:: VoidPtr(address, size = -1, writeable = 0) :param address: (PyBuffer, SbkObject, int, VoidPtr) diff --git a/sources/shiboken6/doc/typediscovery.rst b/sources/shiboken6/doc/typediscovery.rst new file mode 100644 index 000000000..76d3adf7b --- /dev/null +++ b/sources/shiboken6/doc/typediscovery.rst @@ -0,0 +1,145 @@ +.. _typediscovery: + +************** +Type Discovery +************** + +When converting objects which are part of a class hierarchy from a pointer to a +base class, it is expected to get the Python type of the actual, most derived +type, as opposed to C++ which requires a cast for this: + +.. code-block:: python + + def event(self, event): + if event.type() == QEvent.Type.MousePress: + self.do_things(event.position()) + ... + + +.. code-block:: c++ + + bool event(QEvent *event) override + { + if (event->type() == QEvent::MousePress) { + auto *mouseEvent = static_cast<QMouseEvent *>(event); + doThings(mouseEvent->position()); + ... + } + +The process of determining the type of the event is called `type discovery`. + +Shiboken generates code to automatically detect the type. First, it tries to +find a converter for the name obtained by ``typeid(*pointer).name()``. This +should normally work as this name is registered by the binding. If that fails, +it starts walking a type inheritance graph built up in libshiboken to find the +most derived class by using a cast function (``dynamic_cast<>`` by default) to +check. + +For normal class hierarchies with virtual destructors, no special handling +is required since ``typeid()`` usually detects the proper class name. + +Multiple inheritance +==================== + +In case of multiple inheritance in C++, the conversion to the derived class is +not done in case it is not a single-line direct inheritance. For example, in +Qt, the class ``QWidget`` inherits both ``QObject`` (base of the ``QObject`` +hierarchy) and ``QPaintDevice``. + +When calling a function returning a ``QPaintDevice *``, for example +``QPainter.device()``, a Python type representing ``QPaintDevice`` is returned +instead of the underlying widget type. This restriction exists because the +underlying pointer in C++ is a pointer to a ``QPaintDevice *`` and differs from +the pointer to the ``QWidget``. + +Hierarchies of classes with non-virtual destructors +=================================================== + +There are some hierarchies of value-ish C++ classes that do not have virtual +destructors. This makes type discovery based on ``typeid()`` and +``dynamic_cast<>`` impossible. + +Examples in Qt are the ``QStyleOption``-derived or the ``QGradient`` +-derived classes. + +For such classes, some attributes need to be specified on the type entries: + +Primarily, a :ref:`polymorphic-id-expression` attribute +must be specified to be used as a check replacing ``dynamic_cast<>``. + +In addition, a :ref:`polymorphic-name-function` attribute can be specified. +This replaces the type name guess obtained by ``typeid()`` and is mainly a hint +to speed things up by skipping the checks for each type in the inheritance +graph. + +A :ref:`polymorphic-base` attribute identifies the base class of a hierarchy. +It should be given in case the base class inherits from another class to +prevent the logic from going below the base class. + +Using type discovery attributes for class hierarchies with virtual destructors +============================================================================== + +It is possible to use :ref:`polymorphic-id-expression` and +:ref:`polymorphic-name-function` for normal class hierarchies with virtual +destructors as well since they basically replace ``typeid()`` and +``dynamic_cast<>``. This makes sense if expressions can be specified that are +faster than the checks on virtual tables. + +Specifying :ref:`polymorphic-base` can also make sense for generating special +cast functions in case of multiple inheritance. For example, in Qt, +``QWindow``, ``QLayout``, ``QWidget`` are base classes of hierarchies. Since +they all inherit from ``QObject``, indicating the base classes prevents +the logic from using ``QObject`` as a base class. + +.. _typediscovery-attributes: + +Type discovery attributes reference +=================================== + +The following attributes related to type discovery may be be specified on the +:ref:`object-type` or :ref:`value-type` elements: + +.. _polymorphic-id-expression: + +polymorphic-id-expression ++++++++++++++++++++++++++ + +The **polymorphic-id-expression** attribute specifies an expression checking +whether a base class pointer is of the matching type. For example, in a +``virtual eventHandler(BaseEvent *e)`` function, this is used to construct a +Python wrapper matching the derived class (for example, a ``MouseEvent`` or +similar). The attribute value may contain placeholders: + +%1 + Fully qualified class name + +%B + Fully qualified name of the base class (found by base class + search or as indicated by **polymorphic-base**). + +To check for a class inheriting ``BaseEvent``, specify: + +.. code-block:: xml + + <object-type name="MouseEvent" + polymorphic-id-expression="%B->type() == BaseEvent::MouseEvent"/> + +.. _polymorphic-name-function: + +polymorphic-name-function ++++++++++++++++++++++++++ + +The **polymorphic-name-function** attribute specifies the name of a function +returning the type name of a derived class on the base class type entry. +Normally, ``typeid(ptr).name()`` is used for this. + +The function is expected to return ``const char *``. + +.. _polymorphic-base: + +polymorphic-base +++++++++++++++++ + +The boolean **polymorphic-base** attribute indicates whether the class is the +base class of a class hierarchy. It is used for the *%B* placeholder in +**polymorphic-id-expression** and for cast operations in multiple inheritance. diff --git a/sources/shiboken6/doc/typesystem.rst b/sources/shiboken6/doc/typesystem.rst index e1e4fdda2..26f929801 100644 --- a/sources/shiboken6/doc/typesystem.rst +++ b/sources/shiboken6/doc/typesystem.rst @@ -65,3 +65,4 @@ Extra options and Python caveats typesystem_solving_compilation.rst typesystem_specialfunctions.rst + typediscovery.rst diff --git a/sources/shiboken6/doc/typesystem_converters.rst b/sources/shiboken6/doc/typesystem_converters.rst index 7bdabc49c..ab6fba930 100644 --- a/sources/shiboken6/doc/typesystem_converters.rst +++ b/sources/shiboken6/doc/typesystem_converters.rst @@ -233,61 +233,3 @@ Variables & Functions **%CHECKTYPE[CPPTYPE]** Replaced by a |project| type checking function for a Python variable. The C++ type is indicated by ``CPPTYPE``. - - -.. _oldconverters: - -Converting The Old Converters -============================= - -If you use |project| for your bindings, and has defined some type conversions -using the ``Shiboken::Converter`` template, then you must update your converters -to the new scheme. - -Previously your conversion rules were declared in one line, like this: - - -.. code-block:: xml - - <primitive-type name="Complex" target-lang-api-name="PyComplex"> - <include file-name="complex.h" location="global"/> - <conversion-rule file="complex_conversions.h"/> - </primitive-type> - - -And implemented in a separate C++ file, like this: - - -.. code-block:: c++ - - namespace Shiboken { - template<> struct Converter<Complex> - { - static inline bool checkType(PyObject* pyObj) { - return PyComplex_Check(pyObj); - } - static inline bool isConvertible(PyObject* pyObj) { - return PyComplex_Check(pyObj); - } - static inline PyObject* toPython(void* cppobj) { - return toPython(*reinterpret_cast<Complex*>(cppobj)); - } - static inline PyObject* toPython(const Complex& cpx) { - return PyComplex_FromDoubles(cpx.real(), cpx.imag()); - } - static inline Complex toCpp(PyObject* pyobj) { - double real = PyComplex_RealAsDouble(pyobj); - double imag = PyComplex_ImagAsDouble(pyobj); - return Complex(real, imag); - } - }; - } - - -In this case, the parts of the implementation that will be used in the new -conversion-rule are the ones in the two last method -``static inline PyObject* toPython(const Complex& cpx)`` and -``static inline Complex toCpp(PyObject* pyobj)``. The ``isConvertible`` method -is gone, and the ``checkType`` is now an attribute of the :ref:`add-conversion <add-conversion>` -tag. Refer back to the first example in this page and you will be able to -correlate the above template with the new scheme of conversion rule definition. diff --git a/sources/shiboken6/doc/typesystem_specifying_types.rst b/sources/shiboken6/doc/typesystem_specifying_types.rst index 66e68ae2b..e979c4ee2 100644 --- a/sources/shiboken6/doc/typesystem_specifying_types.rst +++ b/sources/shiboken6/doc/typesystem_specifying_types.rst @@ -536,37 +536,8 @@ type system has this attribute set, the heuristics will be applied to all classes. In shiboken 7, it will be mandatory to set the attribute. -The *optional* **polymorphic-id-expression** attribute specifies an -expression checking whether a base class pointer is of the matching -type. For example, in a ``virtual eventHandler(BaseEvent *e)`` -function, this is used to construct a Python wrapper matching -the derived class (for example, a ``MouseEvent`` or similar). -The attribute value may contain placeholders: - -%1 - Fully qualified class name - -%B - Fully qualified name of the base class (found by base class - search or as indicated by **polymorphic-base**). - -To check for a class inheriting ``BaseEvent``, specify: - -.. code-block:: xml - - <object-type name="MouseEvent" - polymorphic-id-expression="%B->type() == BaseEvent::MouseEvent"/> - -The *optional* **polymorphic-name-function** specifies the name of a -function returning the type name of a derived class on the base class -type entry. Normally, ``typeid(ptr).name()`` is used for this. -However, this fails if the type hierarchy does not have virtual functions. -In this case, a function is required which typically decides depending -on some type enumeration. - -The *optional* **polymorphic-base** attribute indicates -whether the class is the base class of a class hierarchy -(used for the *%B* placeholder in **polymorphic-id-expression**). +For the *optional* **polymorphic-id-expression**, **polymorphic-name-function** +and **polymorphic-base** attributes, see :ref:`typediscovery-attributes`. interface-type ^^^^^^^^^^^^^^ @@ -750,6 +721,9 @@ found in the code will be generated. The type name might optionally be followed an equal sign and the Python type name, for example ``instantiations="int=IntPtr,double=DoublePtr"``. It is also possible to specify a namespace delimited by ``::``. +By default, the type will be in the namespace of the smart pointer, +for example, ``std`` for ``std::shared_ptr``. Preceding +the type name by ``::`` causes it to be in the global namespace. The *optional* attribute **type** specifies the type: diff --git a/sources/shiboken6/generator/generator.cpp b/sources/shiboken6/generator/generator.cpp index b224858c5..a01326530 100644 --- a/sources/shiboken6/generator/generator.cpp +++ b/sources/shiboken6/generator/generator.cpp @@ -231,10 +231,9 @@ QString Generator::getFileNameBaseForSmartPointer(const AbstractMetaType &smartP const AbstractMetaType innerType = smartPointerType.getSmartPointerInnerType(); smartPointerType.typeEntry()->qualifiedCppName(); QString fileName = smartPointerType.typeEntry()->qualifiedCppName().toLower(); - fileName.replace(u"::"_s, u"_"_s); - fileName.append(u"_"_s); + fileName.append(u'_'); fileName.append(innerType.name().toLower()); - + fileName.replace(u"::"_s, u"_"_s); // std::shared_ptr<std::string> return fileName; } diff --git a/sources/shiboken6/generator/qtdoc/qtdocgenerator.cpp b/sources/shiboken6/generator/qtdoc/qtdocgenerator.cpp index 2797ff254..1634a7e83 100644 --- a/sources/shiboken6/generator/qtdoc/qtdocgenerator.cpp +++ b/sources/shiboken6/generator/qtdoc/qtdocgenerator.cpp @@ -970,8 +970,10 @@ static QStringList enumListToToc(const AbstractMetaEnumList &enums) static QChar sortKey(const QString &key) { const auto size = key.size(); - if (size >= 2 && (key.at(0) == u'Q' || key.at(0) == u'q') && key.at(1).isUpper()) - return key.at(1); // "QClass" -> 'C', "qSin()" -> 'S' + if (size >= 2 && (key.at(0) == u'Q' || key.at(0) == u'q') + && (key.at(1).isUpper() || key.at(1).isDigit())) { + return key.at(1); // "QClass" -> 'C', "qSin()" -> 'S', 'Q3DSurfaceWidget' -> '3' + } if (size >= 3 && key.startsWith("Q_"_L1)) return key.at(2).toUpper(); // "Q_ARG" -> 'A' if (size >= 4 && key.startsWith("QT_"_L1)) @@ -1564,3 +1566,26 @@ QtXmlToSphinxLink QtDocGenerator::resolveLink(const QtXmlToSphinxLink &link) con } return resolved; } + +QtXmlToSphinxDocGeneratorInterface::Image + QtDocGenerator::resolveImage(const QString &href, const QString &context) const +{ + QString relativeSourceDir = href; + const QString source = m_options.parameters.docDataDir + u'/' + relativeSourceDir; + if (!QFileInfo::exists(source)) + throw Exception(msgCannotFindImage(href, context,source)); + + // Determine target directory from context, "Pyside2.QtGui.QPainter" ->"Pyside2/QtGui". + // FIXME: Not perfect yet, should have knowledge about namespaces (DataVis3D) or + // nested classes "Pyside2.QtGui.QTouchEvent.QTouchPoint". + QString relativeTargetDir = context; + const auto lastDot = relativeTargetDir.lastIndexOf(u'.'); + if (lastDot != -1) + relativeTargetDir.truncate(lastDot); + relativeTargetDir.replace(u'.', u'/'); + if (!relativeTargetDir.isEmpty()) + relativeTargetDir += u'/'; + relativeTargetDir += href; + + return {relativeSourceDir, relativeTargetDir}; +} diff --git a/sources/shiboken6/generator/qtdoc/qtdocgenerator.h b/sources/shiboken6/generator/qtdoc/qtdocgenerator.h index 3b1c82e74..56e15e2a1 100644 --- a/sources/shiboken6/generator/qtdoc/qtdocgenerator.h +++ b/sources/shiboken6/generator/qtdoc/qtdocgenerator.h @@ -48,6 +48,7 @@ public: const QString &methodName) const override; const QLoggingCategory &loggingCategory() const override; QtXmlToSphinxLink resolveLink(const QtXmlToSphinxLink &) const override; + Image resolveImage(const QString &href, const QString &context) const override; static QString getFuncName(const AbstractMetaFunctionCPtr &cppFunc); static QString formatArgs(const AbstractMetaFunctionCPtr &func); diff --git a/sources/shiboken6/generator/qtdoc/qtxmltosphinx.cpp b/sources/shiboken6/generator/qtdoc/qtxmltosphinx.cpp index 55c1d2090..b8fec836c 100644 --- a/sources/shiboken6/generator/qtdoc/qtxmltosphinx.cpp +++ b/sources/shiboken6/generator/qtdoc/qtxmltosphinx.cpp @@ -1240,36 +1240,17 @@ WebXmlTag QtXmlToSphinx::parentTag() const // Copy images that are placed in a subdirectory "images" under the webxml files // by qdoc to a matching subdirectory under the "rst/PySide6/<module>" directory -static bool copyImage(const QString &href, const QString &docDataDir, - const QString &context, const QString &outputDir, +static bool copyImage(const QString &docDataDir, const QString &relativeSourceFile, + const QString &outputDir, const QString &relativeTargetFile, const QLoggingCategory &lc, QString *errorMessage) { - const QChar slash = u'/'; - const auto lastSlash = href.lastIndexOf(slash); - const QString imagePath = lastSlash != -1 ? href.left(lastSlash) : QString(); - const QString imageFileName = lastSlash != -1 ? href.right(href.size() - lastSlash - 1) : href; - QFileInfo imageSource(docDataDir + slash + href); - if (!imageSource.exists()) { - QTextStream(errorMessage) << "Image " << href << " does not exist in " - << QDir::toNativeSeparators(docDataDir); - return false; - } - // Determine directory from context, "Pyside2.QtGui.QPainter" ->"Pyside2/QtGui". - // FIXME: Not perfect yet, should have knowledge about namespaces (DataVis3D) or - // nested classes "Pyside2.QtGui.QTouchEvent.QTouchPoint". - QString relativeTargetDir = context; - const auto lastDot = relativeTargetDir.lastIndexOf(u'.'); - if (lastDot != -1) - relativeTargetDir.truncate(lastDot); - relativeTargetDir.replace(u'.', slash); - if (!imagePath.isEmpty()) - relativeTargetDir += slash + imagePath; - - const QString targetDir = outputDir + slash + relativeTargetDir; - const QString targetFileName = targetDir + slash + imageFileName; + QString targetFileName = outputDir + u'/' + relativeTargetFile; if (QFileInfo::exists(targetFileName)) return true; - if (!QFileInfo::exists(targetDir)) { + + QString relativeTargetDir = relativeTargetFile; + relativeTargetDir.truncate(qMax(relativeTargetDir.lastIndexOf(u'/'), qsizetype(0))); + if (!relativeTargetDir.isEmpty() && !QFileInfo::exists(outputDir + u'/' + relativeTargetDir)) { const QDir outDir(outputDir); if (!outDir.mkpath(relativeTargetDir)) { QTextStream(errorMessage) << "Cannot create " << QDir::toNativeSeparators(relativeTargetDir) @@ -1278,28 +1259,29 @@ static bool copyImage(const QString &href, const QString &docDataDir, } } - QFile source(imageSource.absoluteFilePath()); + QFile source(docDataDir + u'/' + relativeSourceFile); if (!source.copy(targetFileName)) { QTextStream(errorMessage) << "Cannot copy " << QDir::toNativeSeparators(source.fileName()) << " to " << QDir::toNativeSeparators(targetFileName) << ": " << source.errorString(); return false; } - qCDebug(lc).noquote().nospace() << __FUNCTION__ << " href=\"" - << href << "\", context=\"" << context << "\", docDataDir=\"" - << docDataDir << "\", outputDir=\"" << outputDir << "\", copied \"" - << source.fileName() << "\"->\"" << targetFileName << '"'; + + qCDebug(lc).noquote().nospace() << __FUNCTION__ << " \"" << relativeSourceFile + << "\"->\"" << relativeTargetFile << '"'; return true; } bool QtXmlToSphinx::copyImage(const QString &href) const { QString errorMessage; - const bool result = - ::copyImage(href, m_parameters.docDataDir, m_context, - m_parameters.outputDirectory, - m_generator->loggingCategory(), - &errorMessage); + const auto imagePaths = m_generator->resolveImage(href, m_context); + const bool result = ::copyImage(m_parameters.docDataDir, + imagePaths.source, + m_parameters.outputDirectory, + imagePaths.target, + m_generator->loggingCategory(), + &errorMessage); if (!result) throw Exception(errorMessage); return result; diff --git a/sources/shiboken6/generator/qtdoc/qtxmltosphinxinterface.h b/sources/shiboken6/generator/qtdoc/qtxmltosphinxinterface.h index 16eefad83..d4a098a12 100644 --- a/sources/shiboken6/generator/qtdoc/qtxmltosphinxinterface.h +++ b/sources/shiboken6/generator/qtdoc/qtxmltosphinxinterface.h @@ -53,6 +53,15 @@ public: virtual QtXmlToSphinxLink resolveLink(const QtXmlToSphinxLink &) const = 0; + // Resolve images paths relative to doc data directory/output directory. + struct Image + { + QString source; + QString target; + }; + + virtual Image resolveImage(const QString &href, const QString &context) const = 0; + virtual ~QtXmlToSphinxDocGeneratorInterface() = default; }; diff --git a/sources/shiboken6/generator/shiboken/cppgenerator.cpp b/sources/shiboken6/generator/shiboken/cppgenerator.cpp index 84d3c93d3..97a38a08d 100644 --- a/sources/shiboken6/generator/shiboken/cppgenerator.cpp +++ b/sources/shiboken6/generator/shiboken/cppgenerator.cpp @@ -59,6 +59,7 @@ using namespace Qt::StringLiterals; static const char shibokenErrorsOccurred[] = "Shiboken::Errors::occurred() != nullptr"; static constexpr auto virtualMethodStaticReturnVar = "result"_L1; +static constexpr auto initFuncPrefix = "init_"_L1; static constexpr auto sbkObjectTypeF = "SbkObject_TypeF()"_L1; static const char initInheritanceFunction[] = "initInheritance"; @@ -1337,7 +1338,8 @@ void CppGenerator::writeVirtualMethodPythonOverride(TextStream &s, s << "if (" << PYTHON_RETURN_VAR << ".isNull()) {\n" << indent << "// An error happened in python code!\n" - << "Shiboken::Errors::storeErrorOrPrint();\n" + << "Shiboken::Errors::storePythonOverrideErrorOrPrint(\"" + << func->ownerClass()->name() << "\", funcName);\n" << returnStatement.statement << "\n" << outdent << "}\n"; @@ -1590,6 +1592,33 @@ void CppGenerator::writeEnumConverterFunctions(TextStream &s, const AbstractMeta s << '\n'; } +static void writePointerToPythonConverter(TextStream &c, + const AbstractMetaClassCPtr &metaClass, + const QString &typeName, + const QString &cpythonType) +{ + c << "auto *pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(cppIn));\n" + << "if (pyOut) {\n" << indent + << "Py_INCREF(pyOut);\nreturn pyOut;\n" << outdent + << "}\n"; + + const QString nameFunc = metaClass->typeEntry()->polymorphicNameFunction(); + if (nameFunc.isEmpty() && !metaClass->hasVirtualDestructor()) { + c << "return Shiboken::Object::newObjectWithHeuristics(" + << cpythonType << ", const_cast<void *>(cppIn), false);\n"; + return; + } + + c << "auto *tCppIn = reinterpret_cast<const " << typeName << R"( *>(cppIn); +const char *typeName = )"; + if (nameFunc.isEmpty()) + c << "typeid(*tCppIn).name();\n"; + else + c << nameFunc << "(tCppIn);\n"; + c << "return Shiboken::Object::newObjectForPointer(" + << cpythonType << ", const_cast<void *>(cppIn), false, typeName);\n"; +} + void CppGenerator::writeConverterFunctions(TextStream &s, const AbstractMetaClassCPtr &metaClass, const GeneratorContext &classContext) const { @@ -1635,26 +1664,7 @@ void CppGenerator::writeConverterFunctions(TextStream &s, const AbstractMetaClas c << "return PySide::getWrapperForQObject(reinterpret_cast<" << typeName << " *>(const_cast<void *>(cppIn)), " << cpythonType << ");\n"; } else { - c << "auto *pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(cppIn));\n" - << "if (pyOut) {\n" << indent - << "Py_INCREF(pyOut);\nreturn pyOut;\n" << outdent - << "}\n" - << "bool exactType = false;\n" - << "auto *tCppIn = reinterpret_cast<const " << typeName << R"( *>(cppIn); -const char *typeName = )"; - - const QString nameFunc = metaClass->typeEntry()->polymorphicNameFunction(); - if (nameFunc.isEmpty()) - c << "typeid(*tCppIn).name();\n"; - else - c << nameFunc << "(tCppIn);\n"; - c << R"(auto *sbkType = Shiboken::ObjectType::typeForTypeName(typeName); -if (sbkType != nullptr && Shiboken::ObjectType::hasSpecialCastFunction(sbkType)) - exactType = true; -)" - << "PyObject *result = Shiboken::Object::newObject(" << cpythonType - << R"(, const_cast<void *>(cppIn), false, exactType, typeName); -return result;)"; + writePointerToPythonConverter(c, metaClass, typeName, cpythonType); } std::swap(targetTypeName, sourceTypeName); writeCppToPythonFunction(s, c.toString(), sourceTypeName, targetTypeName); @@ -5096,6 +5106,17 @@ QList<PyMethodDefEntry> return result; } +QString CppGenerator::pythonSignature(const AbstractMetaType &type) const +{ + if (type.isSmartPointer() && !type.instantiations().isEmpty()) { + const auto ste = std::static_pointer_cast<const SmartPointerTypeEntry>(type.typeEntry()); + const auto instantiationTe = type.instantiations().constFirst().typeEntry(); + if (auto opt = api().findSmartPointerInstantiation(ste, instantiationTe)) + return opt->specialized->typeEntry()->qualifiedTargetLangName(); + } + return type.pythonSignature(); +} + // Format the type signature of a function parameter QString CppGenerator::signatureParameter(const AbstractMetaArgument &arg) const { @@ -5107,17 +5128,22 @@ QString CppGenerator::signatureParameter(const AbstractMetaArgument &arg) const metaType = *viewOn; s << arg.name() << ':'; - QStringList signatures(metaType.pythonSignature()); + QStringList signatures(pythonSignature(metaType)); // Implicit conversions (C++): Check for converting constructors // "QColor(Qt::GlobalColor)" or conversion operators const AbstractMetaFunctionCList conversions = api().implicitConversions(metaType); for (const auto &f : conversions) { - if (f->isConstructor() && !f->arguments().isEmpty()) - signatures << f->arguments().constFirst().type().pythonSignature(); - else if (f->isConversionOperator()) + if (f->isConstructor() && !f->arguments().isEmpty()) { + // PYSIDE-2712: modified types from converting constructors are not always correct + // candidates if they are modified by the type system reference + if (!f->arguments().constFirst().isTypeModified()) { + signatures << pythonSignature(f->arguments().constFirst().type()); + } + } else if (f->isConversionOperator()) { signatures << f->ownerClass()->fullName(); + } } const qsizetype size = signatures.size(); @@ -5174,7 +5200,7 @@ void CppGenerator::writeSignatureInfo(TextStream &s, const OverloadData &overloa QString returnType = f->pyiTypeReplaced(0); // pyi or modified type if (returnType.isEmpty() && !f->isVoid()) - returnType = f->type().pythonSignature(); + returnType = pythonSignature(f->type()); if (!returnType.isEmpty()) s << "->" << returnType; @@ -5593,6 +5619,9 @@ void CppGenerator::writeClassRegister(TextStream &s, writeConverterRegister(s, metaClass, classContext); s << '\n'; + if (classContext.forSmartPointer()) + writeSmartPointerConverterInitialization(s, classContext.preciseType()); + // class inject-code target/beginning if (!classTypeEntry->codeSnips().isEmpty()) { writeClassCodeSnips(s, classTypeEntry->codeSnips(), @@ -5826,7 +5855,7 @@ void CppGenerator::writeTypeDiscoveryFunction(TextStream &s, } else if (metaClass->isPolymorphic()) { const auto &ancestors = metaClass->allTypeSystemAncestors(); for (const auto &ancestor : ancestors) { - if (ancestor->baseClass()) + if (ancestor->baseClass() && !ancestor->typeEntry()->isPolymorphicBase()) continue; if (ancestor->isPolymorphic()) { s << "if (instanceType == Shiboken::SbkType< " << m_gsp @@ -6028,16 +6057,20 @@ void CppGenerator::writeNbBoolFunction(const GeneratorContext &context, // Write declaration and invocation of the init function for the module init // function. -void CppGenerator::writeInitFunc(TextStream &declStr, TextStream &callStr, - const QString &initFunctionName, - const TypeEntryCPtr &enclosingEntry, - const QString &pythonName, bool lazy) +static void writeInitFuncDecl(TextStream &declStr, + const QString &functionName) { - const QString functionName = "init_"_L1 + initFunctionName; - const bool hasParent = enclosingEntry && enclosingEntry->type() != TypeEntry::TypeSystemType; - declStr << "PyTypeObject *" << functionName << "(PyObject *" - << (hasParent ? "enclosingClass" : "module") << ");\n"; + declStr << "PyTypeObject *" << functionName << "(PyObject *enclosing);\n"; +} +// Write declaration and invocation of the init function for the module init +// function. +void CppGenerator::writeInitFuncCall(TextStream &callStr, + const QString &functionName, + const TypeEntryCPtr &enclosingEntry, + const QString &pythonName, bool lazy) +{ + const bool hasParent = enclosingEntry && enclosingEntry->type() != TypeEntry::TypeSystemType; if (!lazy) { const QString enclosing = hasParent ? "reinterpret_cast<PyObject *>("_L1 + cpythonTypeNameExt(enclosingEntry) + u')' @@ -6046,18 +6079,14 @@ void CppGenerator::writeInitFunc(TextStream &declStr, TextStream &callStr, } else if (hasParent) { const QString &enclosingName = enclosingEntry->name(); const auto parts = QStringView{enclosingName}.split(u"::", Qt::SkipEmptyParts); + const QString namePathPrefix = enclosingEntry->name().replace("::"_L1, "."_L1); callStr << "Shiboken::Module::AddTypeCreationFunction(" - << "module, \"" << pythonName << "\", " << functionName << ", \""; - for (qsizetype i = 0; i < parts.size(); ++i) { - if (i > 0) - callStr << "\", \""; - callStr << parts.at(i); - } - callStr << "\");\n"; + << "module, \"" << parts[0] << "\", " + << functionName << ", \"" << namePathPrefix << '.' << pythonName << "\");\n"; } else { callStr << "Shiboken::Module::AddTypeCreationFunction(" << "module, \"" << pythonName << "\", " - << "init_" << initFunctionName << ");\n"; + << functionName << ");\n"; } } @@ -6114,9 +6143,10 @@ bool CppGenerator::finishGeneration() s_classInitDecl << te->configCondition() << '\n'; s_classPythonDefines << te->configCondition() << '\n'; } - writeInitFunc(s_classInitDecl, s_classPythonDefines, - getSimpleClassInitFunctionName(cls), - targetLangEnclosingEntry(te), cls->name()); + const QString initFunc = initFuncPrefix + getSimpleClassInitFunctionName(cls); + writeInitFuncDecl(s_classInitDecl, initFunc); + writeInitFuncCall(s_classPythonDefines, initFunc, + targetLangEnclosingEntry(te), cls->name()); if (cls->hasStaticFields()) { s_classInitDecl << "PyTypeObject *" << getSimpleClassStaticFieldsInitFunctionName(cls) << "(PyObject *module);\n"; @@ -6133,13 +6163,12 @@ bool CppGenerator::finishGeneration() for (const auto &smp : api().instantiatedSmartPointers()) { GeneratorContext context = contextForSmartPointer(smp.specialized, smp.type); const auto enclosingClass = context.metaClass()->enclosingClass(); - auto enclosingTypeEntry = enclosingClass - ? enclosingClass->typeEntry() - : targetLangEnclosingEntry(smp.type.typeEntry()); + auto enclosingTypeEntry = targetLangEnclosingEntry(smp.specialized->typeEntry()); - writeInitFunc(s_classInitDecl, s_classPythonDefines, - getInitFunctionName(context), - enclosingTypeEntry, smp.type.name()); + const QString initFunc = initFuncPrefix + getInitFunctionName(context); + writeInitFuncDecl(s_classInitDecl, initFunc); + writeInitFuncCall(s_classPythonDefines, + initFunc, enclosingTypeEntry, smp.specialized->name()); includes.insert(smp.type.instantiations().constFirst().typeEntry()->include()); } @@ -6333,18 +6362,6 @@ bool CppGenerator::finishGeneration() s << '\n'; } - // Implicit smart pointers conversions - const auto &smartPointersList = api().instantiatedSmartPointers(); - if (!smartPointersList.isEmpty()) { - s << "// SmartPointers converters.\n\n"; - for (const auto &smp : smartPointersList) { - s << "// C++ to Python conversion for smart pointer type '" - << smp.type.cppSignature() << "'.\n"; - writeSmartPointerConverterFunctions(s, smp.type); - } - s << '\n'; - } - s << "static struct PyModuleDef moduledef = {\n" << " /* m_base */ PyModuleDef_HEAD_INIT,\n" << " /* m_name */ \"" << moduleName() << "\",\n" @@ -6403,9 +6420,14 @@ bool CppGenerator::finishGeneration() s << "{nullptr, nullptr}\n" << outdent << "};\n" << "// The new global structure consisting of (type, name) pairs.\n" - << cppApiVariableName() << " = cppApi;\n" - << "// The backward compatible alias with upper case indexes.\n" - << cppApiVariableNameOld() << " = reinterpret_cast<PyTypeObject **>(cppApi);\n\n"; + << cppApiVariableName() << " = cppApi;\n"; + if (usePySideExtensions()) + s << "QT_WARNING_PUSH\nQT_WARNING_DISABLE_DEPRECATED\n"; + s << "// The backward compatible alias with upper case indexes.\n" + << cppApiVariableNameOld() << " = reinterpret_cast<PyTypeObject **>(cppApi);\n"; + if (usePySideExtensions()) + s << "QT_WARNING_POP\n"; + s << '\n'; } s << "// Create an array of primitive type converters for the current module.\n" @@ -6454,14 +6476,6 @@ bool CppGenerator::finishGeneration() s << '\n'; } - if (!smartPointersList.isEmpty()) { - s << '\n'; - for (const auto &smp : smartPointersList) { - writeSmartPointerConverterInitialization(s, smp.type); - s << '\n'; - } - } - if (!extendedConverters.isEmpty()) { s << '\n'; for (ExtendedConverterData::const_iterator it = extendedConverters.cbegin(), end = extendedConverters.cend(); it != end; ++it) { diff --git a/sources/shiboken6/generator/shiboken/cppgenerator.h b/sources/shiboken6/generator/shiboken/cppgenerator.h index a31c2ca14..5920c9a3a 100644 --- a/sources/shiboken6/generator/shiboken/cppgenerator.h +++ b/sources/shiboken6/generator/shiboken/cppgenerator.h @@ -60,10 +60,10 @@ private: void generateIncludes(TextStream &s, const GeneratorContext &classContext, const IncludeGroupList &includes = {}, const AbstractMetaClassCList &innerClasses = {}) const; - static void writeInitFunc(TextStream &declStr, TextStream &callStr, - const QString &initFunctionName, - const TypeEntryCPtr &enclosingEntry, - const QString &pythonName, bool lazy = true); + static void writeInitFuncCall(TextStream &callStr, + const QString &functionName, + const TypeEntryCPtr &enclosingEntry, + const QString &pythonName, bool lazy = true); static void writeCacheResetNative(TextStream &s, const GeneratorContext &classContext); void writeConstructorNative(TextStream &s, const GeneratorContext &classContext, const AbstractMetaFunctionCPtr &func) const; @@ -413,6 +413,7 @@ private: void writeSignatureInfo(TextStream &s, const OverloadData &overloads) const; QString signatureParameter(const AbstractMetaArgument &arg) const; + QString pythonSignature(const AbstractMetaType &type) const; /// Writes the implementation of all methods part of python sequence protocol void writeSequenceMethods(TextStream &s, const AbstractMetaClassCPtr &metaClass, @@ -550,9 +551,6 @@ private: static bool hasBoolCast(const AbstractMetaClassCPtr &metaClass) { return boolCast(metaClass).has_value(); } - std::optional<AbstractMetaType> - findSmartPointerInstantiation(const SmartPointerTypeEntryCPtr &pointer, - const TypeEntryCPtr &pointee) const; void clearTpFuncs(); static QString chopType(QString s); diff --git a/sources/shiboken6/generator/shiboken/cppgenerator_smartpointer.cpp b/sources/shiboken6/generator/shiboken/cppgenerator_smartpointer.cpp index 1b893640a..44b76f181 100644 --- a/sources/shiboken6/generator/shiboken/cppgenerator_smartpointer.cpp +++ b/sources/shiboken6/generator/shiboken/cppgenerator_smartpointer.cpp @@ -86,18 +86,6 @@ static ComparisonOperatorList smartPointeeComparisons(const GeneratorContext &co return result; } -std::optional<AbstractMetaType> - CppGenerator::findSmartPointerInstantiation(const SmartPointerTypeEntryCPtr &pointer, - const TypeEntryCPtr &pointee) const -{ - for (const auto &smp : api().instantiatedSmartPointers()) { - const auto &i = smp.type; - if (i.typeEntry() == pointer && i.instantiations().at(0).typeEntry() == pointee) - return i; - } - return {}; -} - static bool hasParameterPredicate(const AbstractMetaFunctionCPtr &f) { return !f->arguments().isEmpty(); @@ -225,6 +213,8 @@ void CppGenerator::generateSmartPointerClass(TextStream &s, const GeneratorConte s << '\n'; writeConverterFunctions(s, metaClass, classContext); + // Implicit smart pointers conversions + writeSmartPointerConverterFunctions(s, classContext.preciseType()); writeClassRegister(s, metaClass, classContext, signatureStream); // class inject-code native/end @@ -252,8 +242,8 @@ void CppGenerator::writeSmartPointerConverterFunctions(TextStream &s, for (const auto &base : baseClasses) { auto baseTe = base->typeEntry(); if (smartPointerTypeEntry->matchesInstantiation(baseTe)) { - if (auto opt = findSmartPointerInstantiation(smartPointerTypeEntry, baseTe)) { - const auto smartTargetType = opt.value(); + if (auto opt = api().findSmartPointerInstantiation(smartPointerTypeEntry, baseTe)) { + const auto &smartTargetType = opt.value().type; s << "// SmartPointer derived class: " << smartTargetType.cppSignature() << "\n"; writePythonToCppConversionFunctions(s, smartPointerType, @@ -308,8 +298,8 @@ void CppGenerator::writeSmartPointerConverterInitialization(TextStream &s, for (const auto &base : classes) { auto baseTe = base->typeEntry(); - if (auto opt = findSmartPointerInstantiation(smartPointerTypeEntry, baseTe)) { - const auto smartTargetType = opt.value(); + if (auto opt = api().findSmartPointerInstantiation(smartPointerTypeEntry, baseTe)) { + const auto &smartTargetType = opt.value().type; s << "// Convert to SmartPointer derived class: [" << smartTargetType.cppSignature() << "]\n"; const QString converter = u"Shiboken::Conversions::getConverter(\""_s diff --git a/sources/shiboken6/generator/shiboken/headergenerator.cpp b/sources/shiboken6/generator/shiboken/headergenerator.cpp index 1f574b47c..7cec9c38e 100644 --- a/sources/shiboken6/generator/shiboken/headergenerator.cpp +++ b/sources/shiboken6/generator/shiboken/headergenerator.cpp @@ -198,9 +198,6 @@ void HeaderGenerator::writeWrapperClassDeclaration(TextStream &s, const auto typeEntry = metaClass->typeEntry(); InheritedOverloadSet inheritedOverloads; - // write license comment - s << licenseComment(); - // Class s << "class " << wrapperName << " : public " << metaClass->qualifiedCppName() @@ -706,8 +703,12 @@ bool HeaderGenerator::finishGeneration() macrosStream << ti; macrosStream << "};\n\n"; + // FIXME: Remove backwards compatible variable in PySide 7. macrosStream << "// This variable stores all Python types exported by this module.\n"; macrosStream << "extern Shiboken::Module::TypeInitStruct *" << cppApiVariableName() << ";\n\n"; + macrosStream << "// This variable stores all Python types exported by this module "; + macrosStream << "in a backwards compatible way with identical indexing.\n"; + macrosStream << "[[deprecated]] extern PyTypeObject **" << cppApiVariableNameOld() << ";\n\n"; macrosStream << "// This variable stores the Python module object exported by this module.\n"; macrosStream << "extern PyObject *" << pythonModuleObjectName() << ";\n\n"; macrosStream << "// This variable stores all type converters exported by this module.\n"; diff --git a/sources/shiboken6/generator/shiboken/shibokengenerator.cpp b/sources/shiboken6/generator/shiboken/shibokengenerator.cpp index a1417e5d9..67fd9c994 100644 --- a/sources/shiboken6/generator/shiboken/shibokengenerator.cpp +++ b/sources/shiboken6/generator/shiboken/shibokengenerator.cpp @@ -2555,7 +2555,8 @@ void ShibokenGenerator::collectFullTypeNamesArray(QStringList &typeNames) int smartPointerCountIndex = getMaxTypeIndex(); for (const auto &smp : api().instantiatedSmartPointers()) { auto entry = smp.type.typeEntry(); - typeNames[smartPointerCountIndex] = entry->qualifiedTargetLangName(); + typeNames[smartPointerCountIndex] = + smp.specialized->typeEntry()->qualifiedTargetLangName(); ++smartPointerCountIndex; } } diff --git a/sources/shiboken6/libshiboken/basewrapper.cpp b/sources/shiboken6/libshiboken/basewrapper.cpp index 1ac65c00c..0ce80d0c6 100644 --- a/sources/shiboken6/libshiboken/basewrapper.cpp +++ b/sources/shiboken6/libshiboken/basewrapper.cpp @@ -27,7 +27,9 @@ #include "signature_p.h" #include "voidptr.h" +#include <string> #include <iostream> +#include <sstream> #if defined(__APPLE__) #include <dlfcn.h> @@ -197,31 +199,6 @@ static PyGetSetDef SbkObjectType_tp_getset[] = { static PyTypeObject *createObjectTypeType() { - // PYSIDE-2676: When using the new type extension, we need to use an - // extra meta type that provides the extra size. - // This is a hairy part of Python 3.12 . - // - // The problem here is that we use the type extension both in types - // and also in meta types. This was invisible with extender dicts. - // Please study carefully: - // https://docs.python.org/3/c-api/type.html#c.PyType_Spec.basicsize - - PyType_Slot SbkObjectTypeMeta_Type_slots[] = { - {Py_tp_base, static_cast<void *>(&PyType_Type)}, - {Py_tp_alloc, reinterpret_cast<void *>(PyType_GenericAlloc)}, - {0, nullptr} - }; - - PyType_Spec SbkObjectTypeMeta_Type_spec = { - "1:Shiboken.ObjectTypeMeta", - -long(sizeof(SbkObjectTypePrivate)), - 0, // sizeof(PyMemberDef), not for PyPy without a __len__ defined - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_TYPE_SUBCLASS, - SbkObjectTypeMeta_Type_slots, - }; - - auto specMeta = &SbkObjectTypeMeta_Type_spec; - PyType_Slot SbkObjectType_Type_slots[] = { {Py_tp_dealloc, reinterpret_cast<void *>(SbkObjectType_tp_dealloc)}, {Py_tp_getattro, reinterpret_cast<void *>(mangled_type_getattro)}, @@ -259,14 +236,9 @@ static PyTypeObject *createObjectTypeType() SbkObjectType_Type_slots, }; - if (_PepRuntimeVersion() >= 0x030C00) { - auto *meta = SbkType_FromSpec(specMeta); - auto spec = &SbkObjectType_Type_spec_312; - return SbkType_FromSpecWithMeta(spec, meta); - } - - auto spec = &SbkObjectType_Type_spec; - return SbkType_FromSpec(spec); + return SbkType_FromSpec(_PepRuntimeVersion() >= 0x030C00 ? + &SbkObjectType_Type_spec_312 : + &SbkObjectType_Type_spec); } PyTypeObject *SbkObjectType_TypeF(void) @@ -801,6 +773,19 @@ namespace Shiboken void _initMainThreadId(); // helper.cpp +static std::string msgFailedToInitializeType(const char *description) +{ + std::ostringstream stream; + stream << "[libshiboken] Failed to initialize " << description; + if (auto *error = PepErr_GetRaisedException()) { + if (auto *str = PyObject_Str(error)) + stream << ": " << Shiboken::String::toCString(str); + Py_DECREF(error); + } + stream << '.'; + return stream.str(); +} + namespace Conversions { void init(); } void init() @@ -816,11 +801,13 @@ void init() //Init private data Pep384_Init(); - if (PyType_Ready(SbkObjectType_TypeF()) < 0) - Py_FatalError("[libshiboken] Failed to initialize Shiboken.BaseWrapperType metatype."); + auto *type = SbkObjectType_TypeF(); + if (type == nullptr || PyType_Ready(type) < 0) + Py_FatalError(msgFailedToInitializeType("Shiboken.BaseWrapperType metatype").c_str()); - if (PyType_Ready(SbkObject_TypeF()) < 0) - Py_FatalError("[libshiboken] Failed to initialize Shiboken.BaseWrapper type."); + type = SbkObject_TypeF(); + if (type == nullptr || PyType_Ready(type) < 0) + Py_FatalError(msgFailedToInitializeType("Shiboken.BaseWrapper type").c_str()); VoidPtr::init(); @@ -1010,8 +997,7 @@ introduceWrapperType(PyObject *enclosingObject, PyObject *bases, unsigned wrapperFlags) { - const auto basesSize = PySequence_Fast_GET_SIZE(bases); - assert(basesSize > 0); + assert(PySequence_Fast_GET_SIZE(bases) > 0); typeSpec->slots[0].pfunc = PySequence_Fast_GET_ITEM(bases, 0); auto *type = SbkType_FromSpecBasesMeta(typeSpec, bases, SbkObjectType_TypeF()); @@ -1085,6 +1071,26 @@ bool hasSpecialCastFunction(PyTypeObject *sbkType) return d != nullptr && d->mi_specialcast != nullptr; } +// Find whether base is a direct single line base class of type +// (no multiple inheritance), that is, a C++ pointer cast can safely be done. +static bool isDirectAncestor(PyTypeObject *type, PyTypeObject *base) +{ + if (type == base) + return true; + if (PyTuple_Size(type->tp_bases) == 0) + return false; + auto *sbkObjectType = SbkObject_TypeF(); + auto *firstBase = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(type->tp_bases, 0)); + return firstBase != sbkObjectType + && PyType_IsSubtype(type, sbkObjectType) != 0 + && isDirectAncestor(firstBase, base); +} + +bool canDowncastTo(PyTypeObject *baseType, PyTypeObject *targetType) +{ + return isDirectAncestor(targetType, baseType); +} + } // namespace ObjectType @@ -1454,25 +1460,67 @@ SbkObject *findColocatedChild(SbkObject *wrapper, return nullptr; } +// Legacy, for compatibility only. PyObject *newObject(PyTypeObject *instanceType, void *cptr, bool hasOwnership, bool isExactType, const char *typeName) { - // Try to find the exact type of cptr. - if (!isExactType) { - if (PyTypeObject *exactType = ObjectType::typeForTypeName(typeName)) { - instanceType = exactType; - } else { - auto resolved = BindingManager::instance().findDerivedType(cptr, instanceType); - if (resolved.first != nullptr) { - instanceType = resolved.first; - cptr = resolved.second; - } + return isExactType + ? newObjectForType(instanceType, cptr, hasOwnership) + : newObjectWithHeuristics(instanceType, cptr, hasOwnership, typeName); +} + +static PyObject *newObjectWithHeuristicsHelper(PyTypeObject *instanceType, + PyTypeObject *exactType, + void *cptr, + bool hasOwnership) +{ + // Try to find the exact type of cptr. For hierarchies with + // non-virtual destructors, typeid() will return the base name. + // Try type discovery in these cases. + if (exactType == nullptr || exactType == instanceType) { + auto resolved = BindingManager::instance().findDerivedType(cptr, instanceType); + if (resolved.first != nullptr + && Shiboken::ObjectType::canDowncastTo(instanceType, resolved.first)) { + exactType = resolved.first; + cptr = resolved.second; } } + return newObjectForType(exactType != nullptr ? exactType : instanceType, + cptr, hasOwnership); +} + +PyObject *newObjectForPointer(PyTypeObject *instanceType, + void *cptr, + bool hasOwnership, + const char *typeName) +{ + // Try to find the exact type of cptr. + PyTypeObject *exactType = ObjectType::typeForTypeName(typeName); + // PYSIDE-868: In case of multiple inheritance, (for example, + // a function returning a QPaintDevice * from a QWidget *), + // use instance type to avoid pointer offset errors. + return exactType != nullptr && !Shiboken::ObjectType::canDowncastTo(instanceType, exactType) + ? newObjectForType(instanceType, cptr, hasOwnership) + : newObjectWithHeuristicsHelper(instanceType, exactType, cptr, hasOwnership); +} + + +PyObject *newObjectWithHeuristics(PyTypeObject *instanceType, + void *cptr, + bool hasOwnership, + const char *typeName) +{ + return newObjectWithHeuristicsHelper(instanceType, + ObjectType::typeForTypeName(typeName), + cptr, hasOwnership); +} + +PyObject *newObjectForType(PyTypeObject *instanceType, void *cptr, bool hasOwnership) +{ bool shouldCreate = true; bool shouldRegister = true; SbkObject *self = nullptr; diff --git a/sources/shiboken6/libshiboken/basewrapper.h b/sources/shiboken6/libshiboken/basewrapper.h index 4835c4810..ec5545aea 100644 --- a/sources/shiboken6/libshiboken/basewrapper.h +++ b/sources/shiboken6/libshiboken/basewrapper.h @@ -265,6 +265,14 @@ LIBSHIBOKEN_API PyTypeObject *typeForTypeName(const char *typeName); * \since 5.12 */ LIBSHIBOKEN_API bool hasSpecialCastFunction(PyTypeObject *sbkType); + +/// Returns whether a C++ pointer of \p baseType can be safely downcast +/// to \p targetType (base is a direct, single line base class of targetType). +/// (is a direct, single-line inheritance) +/// \param baseType Python type of base class +/// \param targetType Python type of derived class +/// \since 6.8 +LIBSHIBOKEN_API bool canDowncastTo(PyTypeObject *baseType, PyTypeObject *targetType); } namespace Object { @@ -297,7 +305,8 @@ LIBSHIBOKEN_API SbkObject *findColocatedChild(SbkObject *wrapper, const PyTypeObject *instanceType); /** - * Bind a C++ object to Python. + * Bind a C++ object to Python. Forwards to + * newObjectWithHeuristics(), newObjectForType() depending on \p isExactType. * \param instanceType equivalent Python type for the C++ object. * \param hasOwnership if true, Python will try to delete the underlying C++ object when there's no more refs. * \param isExactType if false, Shiboken will use some heuristics to detect the correct Python type of this C++ @@ -311,6 +320,40 @@ LIBSHIBOKEN_API PyObject *newObject(PyTypeObject *instanceType, bool isExactType = false, const char *typeName = nullptr); +/// Bind a C++ object to Python for polymorphic pointers. Calls +/// newObjectWithHeuristics() with an additional check for multiple +/// inheritance, in which case it will fall back to instanceType. +/// \param instanceType Equivalent Python type for the C++ object. +/// \param hasOwnership if true, Python will try to delete the underlying C++ object +/// when there's no more refs. +/// \param typeName If non-null, this will be used as helper to find the correct +/// Python type for this object (obtained by typeid().name(). +LIBSHIBOKEN_API PyObject *newObjectForPointer(PyTypeObject *instanceType, + void *cptr, + bool hasOwnership = true, + const char *typeName = nullptr); + +/// Bind a C++ object to Python using some heuristics to detect the correct +/// Python type of this C++ object. In any case \p instanceType must be provided; +/// it'll be used as search starting point and as fallback. +/// \param instanceType Equivalent Python type for the C++ object. +/// \param hasOwnership if true, Python will try to delete the underlying C++ object +/// C++ object when there are no more references. +/// when there's no more refs. +/// \param typeName If non-null, this will be used as helper to find the correct +/// Python type for this object (obtained by typeid().name(). +LIBSHIBOKEN_API PyObject *newObjectWithHeuristics(PyTypeObject *instanceType, + void *cptr, + bool hasOwnership = true, + const char *typeName = nullptr); + +/// Bind a C++ object to Python using the given type. +/// \param instanceType Equivalent Python type for the C++ object. +/// \param hasOwnership if true, Python will try to delete the underlying +/// C++ object when there are no more references. +LIBSHIBOKEN_API PyObject *newObjectForType(PyTypeObject *instanceType, + void *cptr, bool hasOwnership = true); + /** * Changes the valid flag of a PyObject, invalid objects will raise an exception when someone tries to access it. */ diff --git a/sources/shiboken6/libshiboken/pep384impl.cpp b/sources/shiboken6/libshiboken/pep384impl.cpp index 4b3759456..5310207a3 100644 --- a/sources/shiboken6/libshiboken/pep384impl.cpp +++ b/sources/shiboken6/libshiboken/pep384impl.cpp @@ -105,13 +105,13 @@ static PyType_Spec typeprobe_spec = { static void check_PyTypeObject_valid() { - auto *obtype = reinterpret_cast<PyObject *>(&PyType_Type); - auto *probe_tp_base = reinterpret_cast<PyTypeObject *>( - PyObject_GetAttr(obtype, Shiboken::PyMagicName::base())); + auto *typetype = &PyType_Type; + auto *obtype = reinterpret_cast<PyObject *>(typetype); + auto *probe_tp_base_obj = PyObject_GetAttr(obtype, Shiboken::PyMagicName::base()); + auto *probe_tp_base = reinterpret_cast<PyTypeObject *>(probe_tp_base_obj); auto *probe_tp_bases = PyObject_GetAttr(obtype, Shiboken::PyMagicName::bases()); - auto *check = reinterpret_cast<PyTypeObject *>( - PyType_FromSpecWithBases(&typeprobe_spec, probe_tp_bases)); - auto *typetype = reinterpret_cast<PyTypeObject *>(obtype); + auto *checkObj = PyType_FromSpecWithBases(&typeprobe_spec, probe_tp_bases); + auto *check = reinterpret_cast<PyTypeObject *>(checkObj); PyObject *w = PyObject_GetAttr(obtype, Shiboken::PyMagicName::weakrefoffset()); long probe_tp_weakrefoffset = PyLong_AsLong(w); PyObject *d = PyObject_GetAttr(obtype, Shiboken::PyMagicName::dictoffset()); @@ -149,8 +149,8 @@ check_PyTypeObject_valid() || probe_tp_mro != typetype->tp_mro || Py_TPFLAGS_DEFAULT != (check->tp_flags & Py_TPFLAGS_DEFAULT)) Py_FatalError("The structure of type objects has changed!"); - Py_DECREF(check); - Py_DECREF(probe_tp_base); + Py_DECREF(checkObj); + Py_DECREF(probe_tp_base_obj); Py_DECREF(w); Py_DECREF(d); Py_DECREF(probe_tp_bases); @@ -482,6 +482,47 @@ Pep_GetVerboseFlag() } #endif // Py_LIMITED_API +// Support for pyerrors.h + +#if defined(Py_LIMITED_API) || PY_VERSION_HEX < 0x030C0000 +// Emulate PyErr_GetRaisedException() using the deprecated PyErr_Fetch()/PyErr_Store() +PyObject *PepErr_GetRaisedException() +{ + PyObject *type{}; + PyObject *value{}; + PyObject *traceback{}; + PyErr_Fetch(&type, &value, &traceback); + Py_XINCREF(value); + PyErr_Restore(type, value, traceback); + return value; +} + +struct PepException_HEAD +{ + PyObject_HEAD + PyObject *x1; // dict + PyObject *args; +}; + +// PyException_GetArgs/PyException_SetArgs were added to the stable API in 3.12 +PyObject *PepException_GetArgs(PyObject *ex) +{ + auto *h = reinterpret_cast<PepException_HEAD *>(ex); + Py_XINCREF(h->args); + return h->args; +} + +LIBSHIBOKEN_API void PepException_SetArgs(PyObject *ex, PyObject *args) +{ + auto *h = reinterpret_cast<PepException_HEAD *>(ex); + Py_XINCREF(args); + auto *old = h->args; // Py_XSETREF() + h->args = args; + Py_XDECREF(old); + +} +#endif // Limited or < 3.12 + /***************************************************************************** * * Support for code.h @@ -722,11 +763,8 @@ PyTypeObject *PepStaticMethod_TypePtr = nullptr; static PyTypeObject * getStaticMethodType(void) { - // this works for Python 3, only - // "StaticMethodType = type(str.__dict__['maketrans'])\n"; static const char prog[] = - "from xxsubtype import spamlist\n" - "result = type(spamlist.__dict__['staticmeth'])\n"; + "result = type(str.__dict__['maketrans'])\n"; return reinterpret_cast<PyTypeObject *>(PepRun_GetResult(prog)); } @@ -1009,9 +1047,12 @@ long _PepRuntimeVersion() SbkObjectTypePrivate *PepType_SOTP(PyTypeObject *type) { + // PYSIDE-2676: Use the meta type explicitly. + // A derived type would fail the offset calculation. + static auto *meta = SbkObjectType_TypeF(); assert(SbkObjectType_Check(type)); auto *obType = reinterpret_cast<PyObject *>(type); - void *data = PyObject_GetTypeData(obType, Py_TYPE(obType)); + void *data = PyObject_GetTypeData(obType, meta); return reinterpret_cast<SbkObjectTypePrivate *>(data); } @@ -1061,11 +1102,12 @@ static thread_local SbkObjectTypePrivate *SOTP_value{}; SbkObjectTypePrivate *PepType_SOTP(PyTypeObject *type) { + static auto *meta = SbkObjectType_TypeF(); static bool use_312 = _PepRuntimeVersion() >= 0x030C00; assert(SbkObjectType_Check(type)); if (use_312) { auto *obType = reinterpret_cast<PyObject *>(type); - void *data = PepObject_GetTypeData(obType, Py_TYPE(obType)); + void *data = PepObject_GetTypeData(obType, meta); return reinterpret_cast<SbkObjectTypePrivate *>(data); } if (type == SOTP_key) diff --git a/sources/shiboken6/libshiboken/pep384impl.h b/sources/shiboken6/libshiboken/pep384impl.h index ec58aac81..7188366e2 100644 --- a/sources/shiboken6/libshiboken/pep384impl.h +++ b/sources/shiboken6/libshiboken/pep384impl.h @@ -188,6 +188,17 @@ LIBSHIBOKEN_API int Pep_GetFlag(const char *name); LIBSHIBOKEN_API int Pep_GetVerboseFlag(void); #endif +// pyerrors.h +#if defined(Py_LIMITED_API) || PY_VERSION_HEX < 0x030C0000 +LIBSHIBOKEN_API PyObject *PepErr_GetRaisedException(); +LIBSHIBOKEN_API PyObject *PepException_GetArgs(PyObject *ex); +LIBSHIBOKEN_API void PepException_SetArgs(PyObject *ex, PyObject *args); +#else +# define PepErr_GetRaisedException PyErr_GetRaisedException +# define PepException_GetArgs PyException_GetArgs +# define PepException_SetArgs PyException_SetArgs +#endif + /***************************************************************************** * * RESOLVED: unicodeobject.h diff --git a/sources/shiboken6/libshiboken/sbkcontainer.h b/sources/shiboken6/libshiboken/sbkcontainer.h index 240c772a9..8ad5aadc6 100644 --- a/sources/shiboken6/libshiboken/sbkcontainer.h +++ b/sources/shiboken6/libshiboken/sbkcontainer.h @@ -74,10 +74,9 @@ public: static PyObject *tpNewInvalid(PyTypeObject * /* subtype */, PyObject * /* args */, PyObject * /* kwds */) { - PyErr_Format(PyExc_NotImplementedError, + return PyErr_Format(PyExc_NotImplementedError, "Opaque containers of type '%s' cannot be instantiated.", typeid(SequenceContainer).name()); - return nullptr; } static int tpInit(PyObject * /* self */, PyObject * /* args */, PyObject * /* kwds */) @@ -105,10 +104,8 @@ public: static PyObject *sqGetItem(PyObject *self, Py_ssize_t i) { auto *d = get(self); - if (i < 0 || i >= Py_ssize_t(d->m_list->size())) { - PyErr_SetString(PyExc_IndexError, "index out of bounds"); - return nullptr; - } + if (i < 0 || i >= Py_ssize_t(d->m_list->size())) + return PyErr_Format(PyExc_IndexError, "index out of bounds"); auto it = std::cbegin(*d->m_list); std::advance(it, i); return ShibokenContainerValueConverter<value_type>::convertValueToPython(*it); @@ -133,14 +130,10 @@ public: static PyObject *push_back(PyObject *self, PyObject *pyArg) { auto *d = get(self); - if (!ShibokenContainerValueConverter<value_type>::checkValue(pyArg)) { - PyErr_SetString(PyExc_TypeError, "wrong type passed to append."); - return nullptr; - } - if (d->m_const) { - PyErr_SetString(PyExc_TypeError, msgModifyConstContainer); - return nullptr; - } + if (!ShibokenContainerValueConverter<value_type>::checkValue(pyArg)) + return PyErr_Format(PyExc_TypeError, "wrong type passed to append."); + if (d->m_const) + return PyErr_Format(PyExc_TypeError, msgModifyConstContainer); OptionalValue value = ShibokenContainerValueConverter<value_type>::convertValueToCpp(pyArg); if (!value.has_value()) @@ -152,14 +145,10 @@ public: static PyObject *push_front(PyObject *self, PyObject *pyArg) { auto *d = get(self); - if (!ShibokenContainerValueConverter<value_type>::checkValue(pyArg)) { - PyErr_SetString(PyExc_TypeError, "wrong type passed to append."); - return nullptr; - } - if (d->m_const) { - PyErr_SetString(PyExc_TypeError, msgModifyConstContainer); - return nullptr; - } + if (!ShibokenContainerValueConverter<value_type>::checkValue(pyArg)) + return PyErr_Format(PyExc_TypeError, "wrong type passed to append."); + if (d->m_const) + return PyErr_Format(PyExc_TypeError, msgModifyConstContainer); OptionalValue value = ShibokenContainerValueConverter<value_type>::convertValueToCpp(pyArg); if (!value.has_value()) @@ -171,10 +160,8 @@ public: static PyObject *clear(PyObject *self) { auto *d = get(self); - if (d->m_const) { - PyErr_SetString(PyExc_TypeError, msgModifyConstContainer); - return nullptr; - } + if (d->m_const) + return PyErr_Format(PyExc_TypeError, msgModifyConstContainer); d->m_list->clear(); Py_RETURN_NONE; @@ -183,10 +170,8 @@ public: static PyObject *pop_back(PyObject *self) { auto *d = get(self); - if (d->m_const) { - PyErr_SetString(PyExc_TypeError, msgModifyConstContainer); - return nullptr; - } + if (d->m_const) + return PyErr_Format(PyExc_TypeError, msgModifyConstContainer); d->m_list->pop_back(); Py_RETURN_NONE; @@ -195,10 +180,8 @@ public: static PyObject *pop_front(PyObject *self) { auto *d = get(self); - if (d->m_const) { - PyErr_SetString(PyExc_TypeError, msgModifyConstContainer); - return nullptr; - } + if (d->m_const) + return PyErr_Format(PyExc_TypeError, msgModifyConstContainer); d->m_list->pop_front(); Py_RETURN_NONE; @@ -208,21 +191,16 @@ public: static PyObject *reserve(PyObject *self, PyObject *pyArg) { auto *d = get(self); - if (PyLong_Check(pyArg) == 0) { - PyErr_SetString(PyExc_TypeError, "wrong type passed to reserve()."); - return nullptr; - } - if (d->m_const) { - PyErr_SetString(PyExc_TypeError, msgModifyConstContainer); - return nullptr; - } + if (PyLong_Check(pyArg) == 0) + return PyErr_Format(PyExc_TypeError, "wrong type passed to reserve()."); + if (d->m_const) + return PyErr_Format(PyExc_TypeError, msgModifyConstContainer); if constexpr (ShibokenContainerHasReserve<SequenceContainer>::value) { const Py_ssize_t size = PyLong_AsSsize_t(pyArg); d->m_list->reserve(size); } else { - PyErr_SetString(PyExc_TypeError, "Container does not support reserve()."); - return nullptr; + return PyErr_Format(PyExc_TypeError, "Container does not support reserve()."); } Py_RETURN_NONE; diff --git a/sources/shiboken6/libshiboken/sbkconverter.cpp b/sources/shiboken6/libshiboken/sbkconverter.cpp index 358827aa8..9ab674415 100644 --- a/sources/shiboken6/libshiboken/sbkconverter.cpp +++ b/sources/shiboken6/libshiboken/sbkconverter.cpp @@ -12,7 +12,12 @@ #include "voidptr.h" #include <string> +#include <cstring> +#include <iostream> #include <unordered_map> +#include <unordered_set> +#include <map> +#include <set> static SbkConverter **PrimitiveTypeConverters; @@ -72,6 +77,103 @@ void init() initArrayConverters(); } +static void dumpPyTypeObject(std::ostream &str, PyTypeObject *t) +{ + str << "\nPython type "; + if (t == nullptr) { + str << "<None>"; + return; + } + str << '"' << t->tp_name << '"'; + if (t->tp_base != nullptr && t->tp_base != &PyBaseObject_Type) + str << '(' << t->tp_base->tp_name << ')'; +} + +static void dumpSbkConverter(std::ostream &str, const SbkConverter *c) +{ + str << "SbkConverter " << static_cast<const void *>(c) << ": "; + if (c->pointerToPython != nullptr) + str << ", C++ pointer->Python"; + if (c->copyToPython != nullptr) + str << ", copy->Python"; + if (c->toCppPointerConversion.second != nullptr) + str << ", Python->C++ pointer"; + if (!c->toCppConversions.empty()) + str << ", " << c->toCppConversions.size() << " Python->C++ conversions"; +} + +// Less than operator for a PyTypeObject for dumping the converter map +static bool pyTypeObjectLessThan(const PyTypeObject *t1, const PyTypeObject *t2) +{ + const bool isNull1 = t1 == nullptr; + const bool isNull2 = t2 == nullptr; + if (isNull1 || isNull2) + return isNull1 && !isNull2; + // Internal types (lower case) first + const bool isInternal1 = std::islower(t1->tp_name[0]); + const bool isInternal2 = std::islower(t2->tp_name[0]); + if (isInternal1 != isInternal2) + return !isInternal2; + return std::strcmp(t1->tp_name, t2->tp_name) < 0; +} + +void dumpConverters() +{ + struct PyTypeObjectLess { + + bool operator()(const PyTypeObject *t1, const PyTypeObject *t2) const { + return pyTypeObjectLessThan(t1, t2); + } + }; + + using StringSet = std::set<std::string>; + using SbkConverterNamesMap = std::unordered_map<SbkConverter *, StringSet>; + using PyTypeObjectConverterMap = std::map<PyTypeObject *, SbkConverterNamesMap, + PyTypeObjectLess>; + + auto &str = std::cerr; + + // Sort the entries by the associated PyTypeObjects and converters + PyTypeObjectConverterMap pyTypeObjectConverterMap; + for (const auto &converter : converters) { + auto *sbkConverter = converter.second; + if (sbkConverter == nullptr) { + str << "Non-existent: \"" << converter.first << "\"\n"; + continue; + } + auto *typeObject = sbkConverter->pythonType; + auto typeIt = pyTypeObjectConverterMap.find(typeObject); + if (typeIt == pyTypeObjectConverterMap.end()) + typeIt = pyTypeObjectConverterMap.insert(std::make_pair(typeObject, + SbkConverterNamesMap{})).first; + SbkConverterNamesMap &sbkConverterMap = typeIt->second; + auto convIt = sbkConverterMap.find(sbkConverter); + if (convIt == sbkConverterMap.end()) + convIt = sbkConverterMap.insert(std::make_pair(sbkConverter, + StringSet{})).first; + convIt->second.insert(converter.first); + } + + for (const auto &tc : pyTypeObjectConverterMap) { + dumpPyTypeObject(str, tc.first); + str << ", " << tc.second.size() << " converter(s):\n"; + for (const auto &cn : tc.second) { + str << " "; + dumpSbkConverter(str, cn.first); + str << ", " << cn.second.size() << " alias(es):"; + int i = 0; + for (const auto &name : cn.second) { + if ((i++ % 5) == 0) + str << "\n "; + str << " \"" << name << '"'; + } + str << '\n'; + } + } + + str << '\n'; +} + SbkConverter *createConverterObject(PyTypeObject *type, PythonToCppFunc toCppPointerConvFunc, IsConvertibleToCppFunc toCppPointerCheckFunc, @@ -422,18 +524,36 @@ void registerConverterName(SbkConverter *converter, const char *typeName) converters.insert(std::make_pair(typeName, converter)); } -static std::string getRealTypeName(const char *name) +static std::string getRealTypeName(const std::string &typeName) { - std::string typeName(name); auto size = typeName.size(); if (std::isalnum(typeName[size - 1]) == 0) return typeName.substr(0, size - 1); return typeName; } -SbkConverter *getConverter(const char *typeName) +// PYSIDE-2404: Build a negative cache of already failed lookups. +// The resulting list must be reset after each new import, +// because that can change results. Also clear the cache after +// reaching some threashold. +static std::unordered_set<std::string> nonExistingTypeNames{}; + +// Arbitrary size limit to prevent random name overflows. +static constexpr std::size_t negativeCacheLimit = 50; + +static void rememberAsNonexistent(const std::string &typeName) +{ + if (nonExistingTypeNames.size() > negativeCacheLimit) + clearNegativeLazyCache(); + converters.insert(std::make_pair(typeName, nullptr)); + nonExistingTypeNames.insert(typeName); +} + +SbkConverter *getConverter(const char *typeNameC) { + std::string typeName = typeNameC; auto it = converters.find(typeName); + // PYSIDE-2404: This can also contain explicit nullptr as a negative cache. if (it != converters.end()) return it->second; // PYSIDE-2404: Did not find the name. Load the lazy classes @@ -442,6 +562,9 @@ SbkConverter *getConverter(const char *typeName) it = converters.find(typeName); if (it != converters.end()) return it->second; + // Cache the negative result. Don't forget to clear the cache for new modules. + rememberAsNonexistent(typeName); + if (Shiboken::pyVerbose() > 0) { const std::string message = std::string("Can't find type resolver for type '") + typeName + "'."; @@ -450,6 +573,15 @@ SbkConverter *getConverter(const char *typeName) return nullptr; } +void clearNegativeLazyCache() +{ + for (const auto &typeName : nonExistingTypeNames) { + auto it = converters.find(typeName); + converters.erase(it); + } + nonExistingTypeNames.clear(); +} + SbkConverter *primitiveTypeConverter(int index) { return PrimitiveTypeConverters[index]; @@ -704,14 +836,7 @@ PyTypeObject *getPythonTypeObject(const SbkConverter *converter) PyTypeObject *getPythonTypeObject(const char *typeName) { - auto *type = getPythonTypeObject(getConverter(typeName)); - if (type == nullptr) { - // PYSIDE-2404: Did not find the name. Load the lazy classes - // which have this name and try again. - Shiboken::Module::loadLazyClassesWithName(getRealTypeName(typeName).c_str()); - type = getPythonTypeObject(getConverter(typeName)); - } - return type; + return getPythonTypeObject(getConverter(typeName)); } bool pythonTypeIsValueType(const SbkConverter *converter) diff --git a/sources/shiboken6/libshiboken/sbkconverter_p.h b/sources/shiboken6/libshiboken/sbkconverter_p.h index c886c9b9f..08fc4c8e1 100644 --- a/sources/shiboken6/libshiboken/sbkconverter_p.h +++ b/sources/shiboken6/libshiboken/sbkconverter_p.h @@ -531,6 +531,12 @@ SbkConverter *createConverterObject(PyTypeObject *type, IsConvertibleToCppFunc toCppPointerCheckFunc, CppToPythonFunc pointerToPythonFunc, CppToPythonFunc copyToPythonFunc); + +LIBSHIBOKEN_API void dumpConverters(); + +/// Interface for sbkmodule which must reset cache when new module is loaded. +LIBSHIBOKEN_API void clearNegativeLazyCache(); + } // namespace Shiboken::Conversions #endif // SBK_CONVERTER_P_H diff --git a/sources/shiboken6/libshiboken/sbkcpptonumpy.cpp b/sources/shiboken6/libshiboken/sbkcpptonumpy.cpp index 44e900f01..7637efa70 100644 --- a/sources/shiboken6/libshiboken/sbkcpptonumpy.cpp +++ b/sources/shiboken6/libshiboken/sbkcpptonumpy.cpp @@ -49,17 +49,17 @@ PyObject *createByteArray1(Py_ssize_t, const uint8_t *) PyObject *createDoubleArray1(Py_ssize_t, const double *) { - return Py_None; + Py_RETURN_NONE; } PyObject *createFloatArray1(Py_ssize_t, const float *) { - return Py_None; + Py_RETURN_NONE; } PyObject *createIntArray1(Py_ssize_t, const int *) { - return Py_None; + Py_RETURN_NONE; } #endif // !HAVE_NUMPY diff --git a/sources/shiboken6/libshiboken/sbkenum.cpp b/sources/shiboken6/libshiboken/sbkenum.cpp index d39369979..4c0597bda 100644 --- a/sources/shiboken6/libshiboken/sbkenum.cpp +++ b/sources/shiboken6/libshiboken/sbkenum.cpp @@ -3,6 +3,7 @@ #include "sbkenum.h" #include "sbkstring.h" +#include "helper.h" #include "sbkstaticstrings.h" #include "sbkstaticstrings_p.h" #include "sbkconverter.h" @@ -306,6 +307,8 @@ static PyTypeObject *createEnumForPython(PyObject *scopeOrModule, enumName = PyDict_GetItem(sotp->enumTypeDict, name); } + SBK_UNUSED(getPyEnumMeta()); // enforce PyEnumModule creation + assert(PyEnumModule != nullptr); AutoDecRef PyEnumType(PyObject_GetAttr(PyEnumModule, enumName)); assert(PyEnumType.object()); bool isFlag = PyObject_IsSubclass(PyEnumType, PyFlag); diff --git a/sources/shiboken6/libshiboken/sbkerrors.cpp b/sources/shiboken6/libshiboken/sbkerrors.cpp index 1832624d5..84c080f8d 100644 --- a/sources/shiboken6/libshiboken/sbkerrors.cpp +++ b/sources/shiboken6/libshiboken/sbkerrors.cpp @@ -6,6 +6,11 @@ #include "helper.h" #include "gilstate.h" +#include <cstdio> +#include <string> + +using namespace std::literals::string_literals; + namespace Shiboken { @@ -93,6 +98,21 @@ void setWrongContainerType() PyErr_SetString(PyExc_TypeError, "Wrong type passed to container conversion."); } +// Prepend something to an exception message provided it is a single string +// argument. +static bool prependToExceptionMessage(PyObject *exc, const char *context) +{ + Shiboken::AutoDecRef args(PepException_GetArgs(exc)); + if (args.isNull() || PyTuple_Check(args.object()) == 0 || PyTuple_Size(args) != 1) + return false; + auto *oldMessage = PyTuple_GetItem(args, 0); + if (oldMessage == nullptr || PyUnicode_CheckExact(oldMessage) == 0) + return false; + auto *newMessage = PyUnicode_FromFormat("%s%U", context, oldMessage); + PepException_SetArgs(exc, PyTuple_Pack(1, newMessage)); + return true; +} + struct ErrorStore { PyObject *type; PyObject *exc; @@ -101,17 +121,42 @@ struct ErrorStore { static thread_local ErrorStore savedError{}; +static bool hasPythonContext() +{ + return _pythonContextStack & 1; +} + void storeErrorOrPrint() { // This error happened in a function with no way to return an error state. // Therefore, we handle the error when we are error checking, anyway. // But we do that only when we know that an error handler can pick it up. - if (_pythonContextStack & 1) + if (hasPythonContext()) PyErr_Fetch(&savedError.type, &savedError.exc, &savedError.traceback); else PyErr_Print(); } +// Like storeErrorOrPrint() with additional context info that is prepended +// to the exception message or printed. +static void storeErrorOrPrintWithContext(const char *context) +{ + if (hasPythonContext()) { + PyErr_Fetch(&savedError.type, &savedError.exc, &savedError.traceback); + prependToExceptionMessage(savedError.exc, context); + } else { + std::fputs(context, stderr); + PyErr_Print(); + } +} + +void storePythonOverrideErrorOrPrint(const char *className, const char *funcName) +{ + const std::string context = "Error calling Python override of "s + + className + "::"s + funcName + "(): "s; + storeErrorOrPrintWithContext(context.c_str()); +} + PyObject *occurred() { if (savedError.type) { diff --git a/sources/shiboken6/libshiboken/sbkerrors.h b/sources/shiboken6/libshiboken/sbkerrors.h index 6ff85f8e1..18ce701e7 100644 --- a/sources/shiboken6/libshiboken/sbkerrors.h +++ b/sources/shiboken6/libshiboken/sbkerrors.h @@ -50,6 +50,11 @@ LIBSHIBOKEN_API void setWrongContainerType(); /// This replaces `PyErr_Print`, which cannot report errors as exception. /// To be used in contexts where raising errors is impossible. LIBSHIBOKEN_API void storeErrorOrPrint(); + +/// Call storeErrorOrPrint() and print the context to report +/// errors when calling Python overrides of virtual functions. +LIBSHIBOKEN_API void storePythonOverrideErrorOrPrint(const char *className, const char *funcName); + /// Handle an error as in PyErr_Occurred(), but also check for errors which /// were captured by `storeErrorOrPrint`. /// To be used in normal error checks. diff --git a/sources/shiboken6/libshiboken/sbkfeature_base.cpp b/sources/shiboken6/libshiboken/sbkfeature_base.cpp index f31b8f4f7..971835c53 100644 --- a/sources/shiboken6/libshiboken/sbkfeature_base.cpp +++ b/sources/shiboken6/libshiboken/sbkfeature_base.cpp @@ -92,8 +92,12 @@ void disassembleFrame(const char *marker) PyErr_Restore(error_type, error_value, error_traceback); } -// python 3.12 -static int const CALL = 171; +// Python 3.13 +static int const LOAD_ATTR_313 = 82; +static int const CALL_313 = 53; +static int const PUSH_NULL_313 = 34; +// Python 3.12 +static int const CALL_312 = 171; // Python 3.11 static int const PRECALL = 166; // we have "big instructions" with gaps after them @@ -105,13 +109,16 @@ static int const LOAD_METHOD = 160; static int const CALL_METHOD = 161; // Python 3.6 static int const CALL_FUNCTION = 131; -static int const LOAD_ATTR = 106; +static int const LOAD_ATTR_312 = 106; // NoGil (how long will this exist in this form?) static int const LOAD_METHOD_NOGIL = 55; static int const CALL_METHOD_NOGIL = 72; static bool currentOpcode_Is_CallMethNoArgs() { + static auto number = _PepRuntimeVersion(); + static int LOAD_ATTR = number < 0x030D00 ? LOAD_ATTR_312 : LOAD_ATTR_313; + static int CALL = number < 0x030D00 ? CALL_312 : CALL_313; // PYSIDE-2221: Special case for the NoGil version: // Find out if we have such a version. // We could also ask the variable `Py_NOGIL`. @@ -148,7 +155,6 @@ static bool currentOpcode_Is_CallMethNoArgs() } uint8_t opcode2 = co_code[f_lasti + 2]; uint8_t oparg2 = co_code[f_lasti + 3]; - static auto number = _PepRuntimeVersion(); if (number < 0x030B00) return opcode1 == LOAD_METHOD && opcode2 == CALL_METHOD && oparg2 == 0; @@ -158,7 +164,7 @@ static bool currentOpcode_Is_CallMethNoArgs() // don't need to take care of them. if (opcode1 == LOAD_METHOD) f_lasti += LOAD_METHOD_GAP_311; - else if (opcode1 == LOAD_ATTR) + else if (opcode1 == LOAD_ATTR_312) f_lasti += LOAD_ATTR_GAP_311; else return false; @@ -176,6 +182,11 @@ static bool currentOpcode_Is_CallMethNoArgs() else return false; + if (number >= 0x030D00) { + int opcode3 = co_code[f_lasti + 2]; + if (opcode3 == PUSH_NULL_313) + f_lasti += 2; + } opcode2 = co_code[f_lasti + 2]; oparg2 = co_code[f_lasti + 3]; diff --git a/sources/shiboken6/libshiboken/sbkmodule.cpp b/sources/shiboken6/libshiboken/sbkmodule.cpp index 4153df27f..a94fbe279 100644 --- a/sources/shiboken6/libshiboken/sbkmodule.cpp +++ b/sources/shiboken6/libshiboken/sbkmodule.cpp @@ -7,21 +7,27 @@ #include "bindingmanager.h" #include "sbkstring.h" #include "sbkcppstring.h" +#include "sbkconverter_p.h" #include <unordered_map> #include <unordered_set> +#include <vector> #include <cstring> +/// This hash maps module objects to arrays of converters. +using ModuleConvertersMap = std::unordered_map<PyObject *, SbkConverter **> ; + /// This hash maps module objects to arrays of Python types. using ModuleTypesMap = std::unordered_map<PyObject *, Shiboken::Module::TypeInitStruct *> ; -/// This hash maps module objects to arrays of converters. -using ModuleConvertersMap = std::unordered_map<PyObject *, SbkConverter **>; +struct TypeCreationStruct +{ + Shiboken::Module::TypeCreationFunction func; + std::vector<std::string> subtypeNames; +}; -/// This hash maps type names to type creation functions. -using TypeCreationFunctionModulePair = - std::pair<Shiboken::Module::TypeCreationFunction, PyObject *>; -using NameToTypeFunctionMap = std::unordered_map<std::string, TypeCreationFunctionModulePair>; +/// This hash maps type names to type creation structs. +using NameToTypeFunctionMap = std::unordered_map<std::string, TypeCreationStruct> ; /// This hash maps module objects to maps of names to functions. using ModuleToFuncsMap = std::unordered_map<PyObject *, NameToTypeFunctionMap> ; @@ -56,8 +62,8 @@ LIBSHIBOKEN_API PyTypeObject *get(TypeInitStruct &typeStruct) AutoDecRef modName(String::fromCppStringView(names.substr(0, dotPos))); auto *modOrType = PyDict_GetItem(sysModules, modName); if (modOrType == nullptr) { - PyErr_Format(PyExc_SystemError, "Module %s should already be in sys.modules", - PyModule_GetName(modOrType)); + PyErr_Format(PyExc_SystemError, "Module \"%U\" should already be in sys.modules", + modName.object()); return nullptr; } @@ -74,6 +80,39 @@ LIBSHIBOKEN_API PyTypeObject *get(TypeInitStruct &typeStruct) return typeStruct.type; } +static void incarnateHelper(PyObject *module, const std::string_view names, + const NameToTypeFunctionMap &nameToFunc) +{ + auto dotPos = names.find('.'); + std::string::size_type startPos = 0; + auto *modOrType{module}; + while (dotPos != std::string::npos) { + auto typeName = names.substr(startPos, dotPos - startPos); + AutoDecRef obTypeName(String::fromCppStringView(typeName)); + modOrType = PyObject_GetAttr(modOrType, obTypeName); + startPos = dotPos + 1; + dotPos = names.find('.', startPos); + } + // now we have the type to create. + auto funcIter = nameToFunc.find(std::string(names)); + // - call this function that returns a PyTypeObject + auto tcStruct = funcIter->second; + auto initFunc = tcStruct.func; + PyTypeObject *type = initFunc(modOrType); + auto name = names.substr(startPos); + PyObject_SetAttrString(modOrType, name.data(), reinterpret_cast<PyObject *>(type)); +} + +static void incarnateSubtypes(PyObject *module, + const std::vector<std::string> &nameList, + NameToTypeFunctionMap &nameToFunc) +{ + for (auto const & tableIter : nameList) { + std::string_view names(tableIter); + incarnateHelper(module, names, nameToFunc); + } +} + static PyTypeObject *incarnateType(PyObject *module, const char *name, NameToTypeFunctionMap &nameToFunc) { @@ -85,13 +124,15 @@ static PyTypeObject *incarnateType(PyObject *module, const char *name, return nullptr; } // - call this function that returns a PyTypeObject - auto pair = funcIter->second; - auto initFunc = pair.first; - auto *modOrType = pair.second; + auto tcStruct = funcIter->second; + auto initFunc = tcStruct.func; + auto *modOrType{module}; // PYSIDE-2404: Make sure that no switching happens during type creation. auto saveFeature = initSelectableFeature(nullptr); PyTypeObject *type = initFunc(modOrType); + if (!tcStruct.subtypeNames.empty()) + incarnateSubtypes(module, tcStruct.subtypeNames, nameToFunc); initSelectableFeature(saveFeature); // - assign this object to the name in the module @@ -164,7 +205,7 @@ static PyObject *PyModule_lazyGetAttro(PyObject *module, PyObject *name) // - locate the name and retrieve the generating function const char *attrNameStr = Shiboken::String::toCString(name); auto &nameToFunc = tableIter->second; - // - create the real type (incarnateType checks this) + // - create the real type and handle subtypes auto *type = incarnateType(module, attrNameStr, nameToFunc); auto *ret = reinterpret_cast<PyObject *>(type); // - if attribute does really not exist use the original @@ -172,7 +213,6 @@ static PyObject *PyModule_lazyGetAttro(PyObject *module, PyObject *name) PyErr_Clear(); return origModuleGetattro(module, name); } - return ret; } @@ -208,6 +248,9 @@ static PyMethodDef module_methods[] = { // Python 3.8 - 3.12 static int const LOAD_CONST_312 = 100; static int const IMPORT_NAME_312 = 108; +// Python 3.13 +static int const LOAD_CONST_313 = 83; +static int const IMPORT_NAME_313 = 75; static bool isImportStar(PyObject *module) { @@ -220,6 +263,9 @@ static bool isImportStar(PyObject *module) static PyObject *const _co_consts = Shiboken::String::createStaticString("co_consts"); static PyObject *const _co_names = Shiboken::String::createStaticString("co_names"); + static int LOAD_CONST = _PepRuntimeVersion() < 0x030D00 ? LOAD_CONST_312 : LOAD_CONST_313; + static int IMPORT_NAME = _PepRuntimeVersion() < 0x030D00 ? IMPORT_NAME_312 : IMPORT_NAME_313; + auto *obFrame = reinterpret_cast<PyObject *>(PyEval_GetFrame()); if (obFrame == nullptr) return true; // better assume worst-case. @@ -239,7 +285,7 @@ static bool isImportStar(PyObject *module) PyBytes_AsStringAndSize(dec_co_code, &co_code, &code_len); uint8_t opcode2 = co_code[f_lasti]; uint8_t opcode1 = co_code[f_lasti - 2]; - if (opcode1 == LOAD_CONST_312 && opcode2 == IMPORT_NAME_312) { + if (opcode1 == LOAD_CONST && opcode2 == IMPORT_NAME) { uint8_t oparg1 = co_code[f_lasti - 1]; uint8_t oparg2 = co_code[f_lasti + 1]; AutoDecRef dec_co_consts(PyObject_GetAttr(dec_f_code, _co_consts)); @@ -260,8 +306,6 @@ static bool isImportStar(PyObject *module) // PYSIDE-2404: These modules produce ambiguous names which we cannot handle, yet. static std::unordered_set<std::string> dontLazyLoad{ - "sample", - "smart", "testbinding" }; @@ -292,24 +336,22 @@ static bool shouldLazyLoad(PyObject *module) return std::strncmp(modName, "PySide6.", 8) == 0; } -void AddTypeCreationFunction(PyObject *module, - const char *name, - TypeCreationFunction func) +static int lazyLoadDefault() { - static const char *flag = getenv("PYSIDE6_OPTION_LAZY"); - static const int value = flag != nullptr ? std::atoi(flag) : 1; +#ifndef PYPY_VERSION + int result = 1; +#else + int result = 0; +#endif + if (auto *flag = getenv("PYSIDE6_OPTION_LAZY")) + result = std::atoi(flag); + return result; +} - // - locate the module in the moduleTofuncs mapping - auto tableIter = moduleToFuncs.find(module); - assert(tableIter != moduleToFuncs.end()); - // - Assign the name/generating function pair. - auto &nameToFunc = tableIter->second; - TypeCreationFunctionModulePair pair{func, module}; - auto nit = nameToFunc.find(name); - if (nit == nameToFunc.end()) - nameToFunc.insert(std::make_pair(name, pair)); - else - nit->second = pair; +void checkIfShouldLoadImmediately(PyObject *module, const std::string &name, + const NameToTypeFunctionMap &nameToFunc) +{ + static const int value = lazyLoadDefault(); // PYSIDE-2404: Lazy Loading // @@ -319,56 +361,56 @@ void AddTypeCreationFunction(PyObject *module, // 3 - lazy loading for any module. // // By default we lazy load all known modules (option = 1). - if (value == 0 // completely disabled || canNotLazyLoad(module) // for some reason we cannot lazy load || (value == 1 && !shouldLazyLoad(module)) // not a known module ) { - PyTypeObject *type = func(module); - PyModule_AddObject(module, name, reinterpret_cast<PyObject *>(type)); // steals reference + incarnateHelper(module, name, nameToFunc); } } void AddTypeCreationFunction(PyObject *module, const char *name, - TypeCreationFunction func, - const char *containerName) + TypeCreationFunction func) { - // This version could be delayed as well, but for the few cases - // we simply fetch the container type and insert directly. - AutoDecRef obContainerType(PyObject_GetAttrString(module, containerName)); - PyTypeObject *type = func(obContainerType); - PyObject_SetAttrString(obContainerType, name, reinterpret_cast<PyObject *>(type)); // steals reference -} + // - locate the module in the moduleTofuncs mapping + auto tableIter = moduleToFuncs.find(module); + assert(tableIter != moduleToFuncs.end()); + // - Assign the name/generating function tcStruct. + auto &nameToFunc = tableIter->second; + TypeCreationStruct tcStruct{func, {}}; + auto nit = nameToFunc.find(name); + if (nit == nameToFunc.end()) + nameToFunc.insert(std::make_pair(name, tcStruct)); + else + nit->second = tcStruct; -void AddTypeCreationFunction(PyObject *module, - const char *name, - TypeCreationFunction func, - const char *outerContainerName, - const char *innerContainerName) -{ - // This version has even more indirection. It is very rare, and - // we handle it directly. - AutoDecRef obOuterType(PyObject_GetAttrString(module, outerContainerName)); - AutoDecRef obInnerType(PyObject_GetAttrString(obOuterType, innerContainerName)); - PyTypeObject *type = func(obInnerType); - PyObject_SetAttrString(obInnerType, name, reinterpret_cast<PyObject *>(type)); // steals reference + checkIfShouldLoadImmediately(module, name, nameToFunc); } void AddTypeCreationFunction(PyObject *module, - const char *name, + const char *containerName, TypeCreationFunction func, - const char *containerName3, - const char *containerName2, - const char *containerName) + const char *namePath) { - // This version has even mode indirection. It is very rare, and - // we handle it directly. - AutoDecRef obContainerType3(PyObject_GetAttrString(module, containerName3)); - AutoDecRef obContainerType2(PyObject_GetAttrString(obContainerType3, containerName2)); - AutoDecRef obContainerType(PyObject_GetAttrString(obContainerType2, containerName)); - PyTypeObject *type = func(obContainerType); - PyObject_SetAttrString(obContainerType, name, reinterpret_cast<PyObject *>(type)); // steals reference + // - locate the module in the moduleTofuncs mapping + auto tableIter = moduleToFuncs.find(module); + assert(tableIter != moduleToFuncs.end()); + // - Assign the name/generating function tcStruct. + auto &nameToFunc = tableIter->second; + auto nit = nameToFunc.find(containerName); + + // - insert namePath into the subtype vector of the main type. + nit->second.subtypeNames.push_back(namePath); + // - insert it also as its own entry. + nit = nameToFunc.find(namePath); + TypeCreationStruct tcStruct{func, {}}; + if (nit == nameToFunc.end()) + nameToFunc.insert(std::make_pair(namePath, tcStruct)); + else + nit->second = tcStruct; + + checkIfShouldLoadImmediately(module, namePath, nameToFunc); } PyObject *import(const char *moduleName) @@ -441,11 +483,11 @@ PyObject *create(const char * /* modName */, void *moduleData) // Install the getattr patch. origModuleGetattro = PyModule_Type.tp_getattro; PyModule_Type.tp_getattro = PyModule_lazyGetAttro; - // Add the lazy import redirection. + // Add the lazy import redirection, keeping a reference. origImportFunc = PyDict_GetItemString(builtins, "__import__"); - auto *func = PyCFunction_NewEx(lazy_methods, nullptr, nullptr); + Py_INCREF(origImportFunc); + AutoDecRef func(PyCFunction_NewEx(lazy_methods, nullptr, nullptr)); PyDict_SetItemString(builtins, "__import__", func); - // Everything is set. lazy_init = true; } // PYSIDE-2404: Nuitka inserts some additional code in standalone mode @@ -455,6 +497,8 @@ PyObject *create(const char * /* modName */, void *moduleData) // into `sys.modules`. This can cause a race condition. // Insert the module early into the module dict to prevend recursion. PyDict_SetItemString(sysModules, PyModule_GetName(module), module); + // Clear the non-existing name cache because we have a new module. + Shiboken::Conversions::clearNegativeLazyCache(); return module; } diff --git a/sources/shiboken6/libshiboken/sbkmodule.h b/sources/shiboken6/libshiboken/sbkmodule.h index 1b3de33b7..2c407e09d 100644 --- a/sources/shiboken6/libshiboken/sbkmodule.h +++ b/sources/shiboken6/libshiboken/sbkmodule.h @@ -56,18 +56,6 @@ LIBSHIBOKEN_API void AddTypeCreationFunction(PyObject *module, TypeCreationFunction func, const char *containerName); -LIBSHIBOKEN_API void AddTypeCreationFunction(PyObject *module, - const char *name, - TypeCreationFunction func, - const char *outerContainerName, - const char *innerContainerName); - -LIBSHIBOKEN_API void AddTypeCreationFunction(PyObject *module, - const char *name, - TypeCreationFunction func, - const char *containerName3, - const char *containerName2, - const char *containerName); /** * Registers the list of types created by \p module. * \param module Module where the types were created. diff --git a/sources/shiboken6/libshiboken/sbknumpy.cpp b/sources/shiboken6/libshiboken/sbknumpy.cpp index 2e1c64d73..b6422e73f 100644 --- a/sources/shiboken6/libshiboken/sbknumpy.cpp +++ b/sources/shiboken6/libshiboken/sbknumpy.cpp @@ -29,10 +29,8 @@ static void initNumPy() // Expanded from macro "import_array" in __multiarray_api.h // Make sure to read about the magic defines PY_ARRAY_UNIQUE_SYMBOL etc., // when changing this or spreading the code over several source files. - if (_import_array() < 0) { + if (_import_array() < 0) PyErr_Print(); - PyErr_Clear(); - } } #endif // HAVE_NUMPY diff --git a/sources/shiboken6/libshiboken/sbkstring.cpp b/sources/shiboken6/libshiboken/sbkstring.cpp index 1471cd7fe..b5e87ca5a 100644 --- a/sources/shiboken6/libshiboken/sbkstring.cpp +++ b/sources/shiboken6/libshiboken/sbkstring.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "sbkstring.h" +#include "sbkenum.h" #include "sbkstaticstrings_p.h" #include "autodecref.h" @@ -14,6 +15,11 @@ bool checkIterable(PyObject *obj) return PyObject_HasAttr(obj, Shiboken::PyMagicName::iter()); } +bool checkIterableArgument(PyObject *obj) +{ + return checkIterable(obj) && !Shiboken::Enum::check(obj); +} + static PyObject *initPathLike() { PyObject *PathLike{}; diff --git a/sources/shiboken6/libshiboken/sbkstring.h b/sources/shiboken6/libshiboken/sbkstring.h index f91847c11..ebc5428c7 100644 --- a/sources/shiboken6/libshiboken/sbkstring.h +++ b/sources/shiboken6/libshiboken/sbkstring.h @@ -13,6 +13,8 @@ namespace String { LIBSHIBOKEN_API bool check(PyObject *obj); LIBSHIBOKEN_API bool checkIterable(PyObject *obj); + /// Check for iterable function arguments (excluding enumerations) + LIBSHIBOKEN_API bool checkIterableArgument(PyObject *obj); LIBSHIBOKEN_API bool checkPath(PyObject *path); LIBSHIBOKEN_API bool checkType(PyTypeObject *obj); LIBSHIBOKEN_API bool checkChar(PyObject *obj); diff --git a/sources/shiboken6/libshiboken/voidptr.cpp b/sources/shiboken6/libshiboken/voidptr.cpp index 7045b08b1..8bb3f6ac8 100644 --- a/sources/shiboken6/libshiboken/voidptr.cpp +++ b/sources/shiboken6/libshiboken/voidptr.cpp @@ -156,10 +156,9 @@ PyObject *SbkVoidPtrObject_int(PyObject *v) PyObject *toBytes(PyObject *self, PyObject * /* args */) { auto *sbkObject = reinterpret_cast<SbkVoidPtrObject *>(self); - if (sbkObject->size < 0) { - PyErr_SetString(PyExc_IndexError, "VoidPtr does not have a size set."); - return nullptr; - } + if (sbkObject->size < 0) + return PyErr_Format(PyExc_IndexError, "VoidPtr does not have a size set."); + PyObject *bytes = PyBytes_FromStringAndSize(reinterpret_cast<const char *>(sbkObject->cptr), sbkObject->size); Py_XINCREF(bytes); diff --git a/sources/shiboken6/shibokenmodule/shibokenmodule.cpp b/sources/shiboken6/shibokenmodule/shibokenmodule.cpp index b3adfe78b..5c6219885 100644 --- a/sources/shiboken6/shibokenmodule/shibokenmodule.cpp +++ b/sources/shiboken6/shibokenmodule/shibokenmodule.cpp @@ -100,6 +100,10 @@ const bool ok = Shiboken::BindingManager::instance().dumpTypeGraph(%1); Shiboken::BindingManager::instance().dumpWrapperMap(); // @snippet dumpwrappermap +// @snippet dumpconverters +Shiboken::Conversions::dumpConverters(); +// @snippet dumpconverters + // @snippet init // Add __version__ and __version_info__ attributes to the module PyObject* version = PyTuple_New(5); diff --git a/sources/shiboken6/shibokenmodule/typesystem_shiboken.xml b/sources/shiboken6/shibokenmodule/typesystem_shiboken.xml index aa08a8bbf..acb522ecc 100644 --- a/sources/shiboken6/shibokenmodule/typesystem_shiboken.xml +++ b/sources/shiboken6/shibokenmodule/typesystem_shiboken.xml @@ -57,9 +57,14 @@ <inject-code file="shibokenmodule.cpp" snippet="dumpwrappermap"/> </add-function> + <add-function signature="dumpConverters()"> + <inject-code file="shibokenmodule.cpp" snippet="dumpconverters"/> + </add-function> + <extra-includes> <include file-name="sbkversion.h" location="local"/> <include file-name="voidptr.h" location="local"/> + <include file-name="sbkconverter_p.h" location="local"/> </extra-includes> <inject-code position="end" file="shibokenmodule.cpp" snippet="init"/> </typesystem> diff --git a/sources/shiboken6/tests/libother/othermultiplederived.h b/sources/shiboken6/tests/libother/othermultiplederived.h index a8e265388..9f90c43a7 100644 --- a/sources/shiboken6/tests/libother/othermultiplederived.h +++ b/sources/shiboken6/tests/libother/othermultiplederived.h @@ -6,11 +6,12 @@ #include "libothermacros.h" #include "multiple_derived.h" +#include "objecttype.h" #include "virtualmethods.h" class ObjectType; -class LIBOTHER_API OtherMultipleDerived : public MDerived1 +class LIBOTHER_API OtherMultipleDerived : public OtherBase, public MDerived1 { public: // this will use CppCopier from other module (bug#142) diff --git a/sources/shiboken6/tests/libsample/derived.h b/sources/shiboken6/tests/libsample/derived.h index b7736c37a..cf95cb601 100644 --- a/sources/shiboken6/tests/libsample/derived.h +++ b/sources/shiboken6/tests/libsample/derived.h @@ -26,7 +26,7 @@ public: public: void uselessMethod() {} SomeInnerClass operator+(const SomeInnerClass &other) { return other; } - bool operator==(const SomeInnerClass &) { return true; } + bool operator==(const SomeInnerClass &) const { return true; } }; explicit Derived(int id = -1) noexcept; diff --git a/sources/shiboken6/tests/libsample/point.cpp b/sources/shiboken6/tests/libsample/point.cpp index b8630eb1e..0a28e877f 100644 --- a/sources/shiboken6/tests/libsample/point.cpp +++ b/sources/shiboken6/tests/libsample/point.cpp @@ -34,7 +34,7 @@ void Point::show() const std::cout << "(x: " << m_x << ", y: " << m_y << ")"; } -bool Point::operator==(const Point &other) +bool Point::operator==(const Point &other) const { return m_x == other.m_x && m_y == other.m_y; } diff --git a/sources/shiboken6/tests/libsample/point.h b/sources/shiboken6/tests/libsample/point.h index 59e0236d5..7e5d128ab 100644 --- a/sources/shiboken6/tests/libsample/point.h +++ b/sources/shiboken6/tests/libsample/point.h @@ -38,7 +38,7 @@ public: // The != operator is not implemented for the purpose of testing // for the absense of the __ne__ method in the Python binding. - bool operator==(const Point &other); + bool operator==(const Point &other) const; Point operator+(const Point &other); Point operator-(const Point &other); diff --git a/sources/shiboken6/tests/libsample/pointf.cpp b/sources/shiboken6/tests/libsample/pointf.cpp index 6b39f73a9..736a5c6b5 100644 --- a/sources/shiboken6/tests/libsample/pointf.cpp +++ b/sources/shiboken6/tests/libsample/pointf.cpp @@ -26,7 +26,7 @@ void PointF::show() const std::cout << "(x: " << m_x << ", y: " << m_y << ")"; } -bool PointF::operator==(const PointF &other) +bool PointF::operator==(const PointF &other) const { return m_x == other.m_x && m_y == other.m_y; } diff --git a/sources/shiboken6/tests/libsample/pointf.h b/sources/shiboken6/tests/libsample/pointf.h index bb50b5c6d..49e009467 100644 --- a/sources/shiboken6/tests/libsample/pointf.h +++ b/sources/shiboken6/tests/libsample/pointf.h @@ -31,7 +31,7 @@ public: // The != operator is not implemented for the purpose of testing // for the absence of the __ne__ method in the Python binding. - bool operator==(const PointF &other); + bool operator==(const PointF &other) const; PointF operator+(const PointF &other); PointF operator-(const PointF &other); diff --git a/sources/shiboken6/tests/libsmart/stdsharedptrtestbench.cpp b/sources/shiboken6/tests/libsmart/stdsharedptrtestbench.cpp index a7b73cc81..472f807f2 100644 --- a/sources/shiboken6/tests/libsmart/stdsharedptrtestbench.cpp +++ b/sources/shiboken6/tests/libsmart/stdsharedptrtestbench.cpp @@ -51,6 +51,46 @@ void StdSharedPtrTestBench::printInt(const std::shared_ptr<int> &p) std::cerr << '\n'; } +std::shared_ptr<double> StdSharedPtrTestBench::createDouble(double v) +{ + return std::make_shared<double>(v); +} + +std::shared_ptr<double> StdSharedPtrTestBench::createNullDouble() +{ + return {}; +} + +void StdSharedPtrTestBench::printDouble(const std::shared_ptr<double> &p) +{ + std::cerr << __FUNCTION__ << ' '; + if (p.get()) + std::cerr << *p; + else + std::cerr << "nullptr"; + std::cerr << '\n'; +} + +std::shared_ptr<std::string> StdSharedPtrTestBench::createString(const char *text) +{ + return std::make_shared<std::string>(text); +} + +std::shared_ptr<std::string> StdSharedPtrTestBench::createNullString() +{ + return {}; +} + +void StdSharedPtrTestBench::printString(const std::shared_ptr<std::string> &p) +{ + std::cerr << __FUNCTION__ << ' '; + if (p.get()) + std::cerr << '"' << *p << '"'; + else + std::cerr << "nullptr"; + std::cerr << '\n'; +} + StdSharedPtrVirtualMethodTester::StdSharedPtrVirtualMethodTester() = default; StdSharedPtrVirtualMethodTester::~StdSharedPtrVirtualMethodTester() = default; diff --git a/sources/shiboken6/tests/libsmart/stdsharedptrtestbench.h b/sources/shiboken6/tests/libsmart/stdsharedptrtestbench.h index 8991cded6..9d4c207b5 100644 --- a/sources/shiboken6/tests/libsmart/stdsharedptrtestbench.h +++ b/sources/shiboken6/tests/libsmart/stdsharedptrtestbench.h @@ -7,6 +7,7 @@ #include "libsmartmacros.h" #include <memory> +#include <string> class Integer; @@ -23,6 +24,14 @@ public: static std::shared_ptr<int> createInt(int v = 42); static std::shared_ptr<int> createNullInt(); static void printInt(const std::shared_ptr<int> &); + + static std::shared_ptr<double> createDouble(double v = 42); + static std::shared_ptr<double> createNullDouble(); + static void printDouble(const std::shared_ptr<double> &); + + static std::shared_ptr<std::string> createString(const char *text); + static std::shared_ptr<std::string> createNullString(); + static void printString(const std::shared_ptr<std::string> &); }; class LIB_SMART_API StdSharedPtrVirtualMethodTester diff --git a/sources/shiboken6/tests/otherbinding/typediscovery_test.py b/sources/shiboken6/tests/otherbinding/typediscovery_test.py index 791d3bdce..39dc5cf0f 100644 --- a/sources/shiboken6/tests/otherbinding/typediscovery_test.py +++ b/sources/shiboken6/tests/otherbinding/typediscovery_test.py @@ -13,7 +13,8 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) from shiboken_paths import init_paths init_paths() -from sample import Abstract, Base1, Derived +from sample import (Abstract, Base1, Derived, + MDerived1, SonOfMDerived1, MDerived3) from other import OtherMultipleDerived @@ -32,14 +33,18 @@ class TypeDiscoveryTest(unittest.TestCase): def testMultipleInheritance(self): obj = OtherMultipleDerived.createObject("Base1") self.assertEqual(type(obj), Base1) - # PYSIDE-868: In case of multiple inheritance, a factory - # function will return the base class wrapper. + # PYSIDE-868: In case of single line direct inheritance, + # a factory function will return the class wrapper + # of the derived class. obj = OtherMultipleDerived.createObject("MDerived1") - self.assertEqual(type(obj), Base1) + self.assertEqual(type(obj), MDerived1) obj = OtherMultipleDerived.createObject("SonOfMDerived1") - self.assertEqual(type(obj), Base1) + self.assertEqual(type(obj), SonOfMDerived1) obj = OtherMultipleDerived.createObject("MDerived3") - self.assertEqual(type(obj), Base1) + self.assertEqual(type(obj), MDerived3) + # PYSIDE-868: OtherMultipleDerived inherits + # OtherBase, Base1. In this case, a factory + # function will return the base class wrapper. obj = OtherMultipleDerived.createObject("OtherMultipleDerived") self.assertEqual(type(obj), Base1) diff --git a/sources/shiboken6/tests/qtxmltosphinx/main.cpp b/sources/shiboken6/tests/qtxmltosphinx/main.cpp index 27aaee7d1..5b0624376 100644 --- a/sources/shiboken6/tests/qtxmltosphinx/main.cpp +++ b/sources/shiboken6/tests/qtxmltosphinx/main.cpp @@ -40,6 +40,7 @@ public: const QString &) const override; const QLoggingCategory &loggingCategory() const override; QtXmlToSphinxLink resolveLink(const QtXmlToSphinxLink &link) const override; + Image resolveImage(const QString &href, const QString &) const; }; // QtXmlToSphinxDocGeneratorInterface @@ -63,11 +64,18 @@ const QLoggingCategory &QtXmlToSphinxDocGenerator::loggingCategory() const return lcQtXmlToSphinx(); } -QtXmlToSphinxLink QtXmlToSphinxDocGenerator::resolveLink(const QtXmlToSphinxLink &link) const +QtXmlToSphinxLink + QtXmlToSphinxDocGenerator::resolveLink(const QtXmlToSphinxLink &link) const { return link; } +QtXmlToSphinxDocGeneratorInterface::Image + QtXmlToSphinxDocGenerator::resolveImage(const QString &href, const QString &) const +{ + return {href, href}; +} + static bool run(const QString &fileName) { QtXmlToSphinxDocGenerator generator; diff --git a/sources/shiboken6/tests/qtxmltosphinxtest/qtxmltosphinxtest.cpp b/sources/shiboken6/tests/qtxmltosphinxtest/qtxmltosphinxtest.cpp index 45cecd1a1..3ba77196f 100644 --- a/sources/shiboken6/tests/qtxmltosphinxtest/qtxmltosphinxtest.cpp +++ b/sources/shiboken6/tests/qtxmltosphinxtest/qtxmltosphinxtest.cpp @@ -39,6 +39,12 @@ QtXmlToSphinxLink QtXmlToSphinxTest::resolveLink(const QtXmlToSphinxLink &link) return link; } +QtXmlToSphinxDocGeneratorInterface::Image + QtXmlToSphinxTest::resolveImage(const QString &href, const QString &) const +{ + return {href, href}; +} + QString QtXmlToSphinxTest::transformXml(const QString &xml) const { return QtXmlToSphinx(this, m_parameters, xml).result(); diff --git a/sources/shiboken6/tests/qtxmltosphinxtest/qtxmltosphinxtest.h b/sources/shiboken6/tests/qtxmltosphinxtest/qtxmltosphinxtest.h index 0a210b7a0..5108ef452 100644 --- a/sources/shiboken6/tests/qtxmltosphinxtest/qtxmltosphinxtest.h +++ b/sources/shiboken6/tests/qtxmltosphinxtest/qtxmltosphinxtest.h @@ -19,6 +19,7 @@ public: const QString &) const override; const QLoggingCategory &loggingCategory() const override; QtXmlToSphinxLink resolveLink(const QtXmlToSphinxLink &link) const override; + Image resolveImage(const QString &href, const QString &context) const override; private slots: void testTable_data(); diff --git a/sources/shiboken6/tests/smartbinding/CMakeLists.txt b/sources/shiboken6/tests/smartbinding/CMakeLists.txt index 2e729321e..594744840 100644 --- a/sources/shiboken6/tests/smartbinding/CMakeLists.txt +++ b/sources/shiboken6/tests/smartbinding/CMakeLists.txt @@ -18,8 +18,10 @@ ${CMAKE_CURRENT_BINARY_DIR}/smart/smart_integer2_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/smart/sharedptr_integer2_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/smart/stdsharedptrtestbench_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/smart/stdsharedptrvirtualmethodtester_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/smart/std_shared_ptr_double_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/smart/std_shared_ptr_integer_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/smart/std_shared_ptr_int_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/smart/std_shared_ptr_std_string_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/smart/std_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/smart/std_optional_int_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/smart/std_optional_integer_wrapper.cpp diff --git a/sources/shiboken6/tests/smartbinding/std_shared_ptr_test.py b/sources/shiboken6/tests/smartbinding/std_shared_ptr_test.py index 2e6aea3d9..a37a307a5 100644 --- a/sources/shiboken6/tests/smartbinding/std_shared_ptr_test.py +++ b/sources/shiboken6/tests/smartbinding/std_shared_ptr_test.py @@ -10,7 +10,7 @@ from pathlib import Path sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) from shiboken_paths import init_paths init_paths() -from smart import Integer, StdSharedPtrTestBench, StdSharedPtrVirtualMethodTester, std +from smart import Integer, StdDoublePtr, StdSharedPtrTestBench, StdSharedPtrVirtualMethodTester, std def call_func_on_ptr(ptr): @@ -49,6 +49,24 @@ class StdSharedPtrTests(unittest.TestCase): self.assertFalse(np) p = StdSharedPtrTestBench.createInt() StdSharedPtrTestBench.printInt(p) + ip = std.StdIntPtr(42) + StdSharedPtrTestBench.printInt(ip) + + def testDouble(self): + np = StdSharedPtrTestBench.createNullDouble() + StdSharedPtrTestBench.printDouble(np) + self.assertFalse(np) + p = StdSharedPtrTestBench.createDouble(67) + StdSharedPtrTestBench.printDouble(p) + dp = StdDoublePtr(42) + StdSharedPtrTestBench.printDouble(dp) + + def testString(self): + np = StdSharedPtrTestBench.createNullString() + StdSharedPtrTestBench.printString(np) + self.assertFalse(np) + p = StdSharedPtrTestBench.createString("bla") + StdSharedPtrTestBench.printString(p) def testVirtuals(self): """Test whether code generating virtual function overrides is generated diff --git a/sources/shiboken6/tests/smartbinding/typesystem_smart.xml b/sources/shiboken6/tests/smartbinding/typesystem_smart.xml index 261d5f15d..e479e4ddf 100644 --- a/sources/shiboken6/tests/smartbinding/typesystem_smart.xml +++ b/sources/shiboken6/tests/smartbinding/typesystem_smart.xml @@ -50,7 +50,7 @@ value-check-method="operator bool" ref-count-method="use_count" reset-method="reset" - instantiations="Integer,int"> + instantiations="Integer,int=StdIntPtr,double=::StdDoublePtr,std::string"> <include file-name="memory" location="global"/> </smart-pointer-type> |