diff options
Diffstat (limited to 'sources/shiboken6/generator/shiboken/cppgenerator.cpp')
-rw-r--r-- | sources/shiboken6/generator/shiboken/cppgenerator.cpp | 2893 |
1 files changed, 1212 insertions, 1681 deletions
diff --git a/sources/shiboken6/generator/shiboken/cppgenerator.cpp b/sources/shiboken6/generator/shiboken/cppgenerator.cpp index c59cfbc11..48e7f4fe5 100644 --- a/sources/shiboken6/generator/shiboken/cppgenerator.cpp +++ b/sources/shiboken6/generator/shiboken/cppgenerator.cpp @@ -2,7 +2,9 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "cppgenerator.h" +#include "configurablescope.h" #include "generatorargument.h" +#include "generatorstrings.h" #include "defaultvalue.h" #include "generatorcontext.h" #include "codesnip.h" @@ -50,14 +52,29 @@ #include <algorithm> #include <cstring> #include <memory> +#include <set> using namespace Qt::StringLiterals; +static const char shibokenErrorsOccurred[] = "Shiboken::Errors::occurred() != nullptr"; + +static constexpr auto virtualMethodStaticReturnVar = "result"_L1; + +static constexpr auto sbkObjectTypeF = "SbkObject_TypeF()"_L1; +static const char initInheritanceFunction[] = "initInheritance"; + +static QString mangleName(QString name) +{ + if (name == u"None" || name == u"False" || name == u"True" || name == u"from") + name += u'_'; + return name; +} + struct sbkUnusedVariableCast { - explicit sbkUnusedVariableCast(QString name) : m_name(name) {} + explicit sbkUnusedVariableCast(QAnyStringView name) : m_name(name) {} - const QString m_name; + const QAnyStringView m_name; }; TextStream &operator<<(TextStream &str, const sbkUnusedVariableCast &c) @@ -66,36 +83,23 @@ TextStream &operator<<(TextStream &str, const sbkUnusedVariableCast &c) return str; } -static const QString CPP_ARG0 = u"cppArg0"_s; -static const char methodDefSentinel[] = "{nullptr, nullptr, 0, nullptr} // Sentinel\n"; -const char *CppGenerator::PYTHON_TO_CPPCONVERSION_STRUCT = "Shiboken::Conversions::PythonToCppConversion"; +struct pyTypeGetSlot +{ + explicit pyTypeGetSlot(QAnyStringView funcType, QAnyStringView typeObject, + QAnyStringView aSlot) : + m_funcType(funcType), m_typeObject(typeObject), m_slot(aSlot) {} -static inline QString reprFunction() { return QStringLiteral("__repr__"); } + const QAnyStringView m_funcType; + const QAnyStringView m_typeObject; + const QAnyStringView m_slot; +}; -static const char typeNameFunc[] = R"CPP(template <class T> -static const char *typeNameOf(const T &t) +TextStream &operator<<(TextStream &str, const pyTypeGetSlot &p) { - const char *typeName = typeid(t).name(); - auto size = std::strlen(typeName); -#if defined(Q_CC_MSVC) // MSVC: "class QPaintDevice * __ptr64" - if (auto lastStar = strchr(typeName, '*')) { - // MSVC: "class QPaintDevice * __ptr64" - while (*--lastStar == ' ') { - } - size = lastStar - typeName + 1; - } -#else // g++, Clang: "QPaintDevice *" -> "P12QPaintDevice" - if (size > 2 && typeName[0] == 'P' && std::isdigit(typeName[1])) { - ++typeName; - --size; - } -#endif - char *result = new char[size + 1]; - result[size] = '\0'; - memcpy(result, typeName, size); - return result; + str << "reinterpret_cast<" << p.m_funcType << ">(PepType_GetSlot(" + << p.m_typeObject << ", " << p.m_slot << "));\n"; + return str; } -)CPP"; TextStream &operator<<(TextStream &s, CppGenerator::ErrorReturn r) { @@ -117,6 +121,25 @@ TextStream &operator<<(TextStream &s, CppGenerator::ErrorReturn r) return s; } +static constexpr auto converterVar = "converter"_L1; + +struct registerConverterName +{ + explicit registerConverterName(QAnyStringView typeName, + QAnyStringView varName = converterVar) : + m_typeName(typeName), m_varName(varName) {} + + QAnyStringView m_typeName; + QAnyStringView m_varName; +}; + +TextStream &operator<<(TextStream &s, const registerConverterName &r) +{ + s << "Shiboken::Conversions::registerConverterName(" << r.m_varName + << ", \"" << r.m_typeName << "\");\n"; + return s; +} + // Protocol function name / function parameters / return type struct ProtocolEntry { @@ -149,7 +172,7 @@ const ProtocolEntries &mappingProtocols() u"PyObject*"_s}, {u"__msetitem__"_s, u"PyObject *self, PyObject *_key, PyObject *_value"_s, - intT()}}; + intT}}; return result; } @@ -166,16 +189,16 @@ const ProtocolEntries &sequenceProtocols() u"PyObject*"_s}, {u"__setitem__"_s, u"PyObject *self, Py_ssize_t _i, PyObject *_value"_s, - intT()}, + intT}, {u"__getslice__"_s, u"PyObject *self, Py_ssize_t _i1, Py_ssize_t _i2"_s, u"PyObject*"_s}, {u"__setslice__"_s, u"PyObject *self, Py_ssize_t _i1, Py_ssize_t _i2, PyObject *_value"_s, - intT()}, + intT}, {u"__contains__"_s, u"PyObject *self, PyObject *_value"_s, - intT()}, + intT}, {u"__concat__"_s, u"PyObject *self, PyObject *_other"_s, u"PyObject*"_s} @@ -187,13 +210,13 @@ const ProtocolEntries &sequenceProtocols() static QString opaqueContainerCreationFunc(const AbstractMetaType &type) { const auto containerTypeEntry = - qSharedPointerCast<const ContainerTypeEntry>(type.typeEntry()); + std::static_pointer_cast<const ContainerTypeEntry>(type.typeEntry()); const auto instantiationTypeEntry = type.instantiations().constFirst().typeEntry(); QString result = u"create"_s; if (type.isConstant()) result += u"Const"_s; - result += containerTypeEntry->opaqueContainerName(instantiationTypeEntry->name()); + result += containerTypeEntry->opaqueContainerName(type.instantiationCppSignatures()); return result; } @@ -213,135 +236,16 @@ QString CppGenerator::fileNameForContext(const GeneratorContext &context) const return fileNameForContextHelper(context, u"_wrapper.cpp"_s); } -static bool isInplaceAdd(const AbstractMetaFunctionCPtr &func) -{ - return func->name() == u"operator+="; -} - -static bool isIncrementOperator(const AbstractMetaFunctionCPtr &func) -{ - return func->functionType() == AbstractMetaFunction::IncrementOperator; -} - -static bool isDecrementOperator(const AbstractMetaFunctionCPtr &func) -{ - return func->functionType() == AbstractMetaFunction::DecrementOperator; -} - -// Filter predicate for operator functions -static bool skipOperatorFunc(const AbstractMetaFunctionCPtr &func) -{ - if (func->isModifiedRemoved() || func->usesRValueReferences()) - return true; - const auto &name = func->name(); - return name == u"operator[]" || name == u"operator->" || name == u"operator!"; -} - -QList<AbstractMetaFunctionCList> - CppGenerator::filterGroupedOperatorFunctions(const AbstractMetaClass *metaClass, - OperatorQueryOptions query) -{ - // ( func_name, num_args ) => func_list - QMap<QPair<QString, int>, AbstractMetaFunctionCList> results; - - auto funcs = metaClass->operatorOverloads(query); - auto end = std::remove_if(funcs.begin(), funcs.end(), skipOperatorFunc); - funcs.erase(end, funcs.end()); - - // If we have operator+=, we remove the operator++/-- which would - // otherwise be used for emulating __iadd__, __isub__. - if (std::any_of(funcs.cbegin(), funcs.cend(), isInplaceAdd)) { - end = std::remove_if(funcs.begin(), funcs.end(), - [] (const AbstractMetaFunctionCPtr &func) { - return func->isIncDecrementOperator(); - }); - funcs.erase(end, funcs.end()); - } else { - // If both prefix/postfix ++/-- are present, remove one - if (std::count_if(funcs.begin(), funcs.end(), isIncrementOperator) > 1) - funcs.erase(std::find_if(funcs.begin(), funcs.end(), isIncrementOperator)); - if (std::count_if(funcs.begin(), funcs.end(), isDecrementOperator) > 1) - funcs.erase(std::find_if(funcs.begin(), funcs.end(), isDecrementOperator)); - } - - for (const auto &func : funcs) { - int args; - if (func->isComparisonOperator()) { - args = -1; - } else { - args = func->arguments().size(); - } - QPair<QString, int > op(func->name(), args); - results[op].append(func); - } - QList<AbstractMetaFunctionCList> result; - result.reserve(results.size()); - for (auto it = results.cbegin(), end = results.cend(); it != end; ++it) - result.append(it.value()); - return result; -} - -CppGenerator::BoolCastFunctionOptional - CppGenerator::boolCast(const AbstractMetaClass *metaClass) const -{ - const auto te = metaClass->typeEntry(); - if (te->isSmartPointer()) { - auto ste = qSharedPointerCast<const SmartPointerTypeEntry>(te); - - auto valueCheckMethod = ste->valueCheckMethod(); - if (!valueCheckMethod.isEmpty()) { - const auto func = metaClass->findFunction(valueCheckMethod); - if (func.isNull()) - throw Exception(msgMethodNotFound(metaClass, valueCheckMethod)); - return BoolCastFunction{func, false}; - } - - auto nullCheckMethod = ste->nullCheckMethod(); - if (!nullCheckMethod.isEmpty()) { - const auto func = metaClass->findFunction(nullCheckMethod); - if (func.isNull()) - throw Exception(msgMethodNotFound(metaClass, nullCheckMethod)); - return BoolCastFunction{func, true}; - } - } - - auto mode = te->operatorBoolMode(); - if (useOperatorBoolAsNbNonZero() - ? mode != TypeSystem::BoolCast::Disabled : mode == TypeSystem::BoolCast::Enabled) { - const auto func = metaClass->findOperatorBool(); - if (!func.isNull()) - return BoolCastFunction{func, false}; - } - - mode = te->isNullMode(); - if (useIsNullAsNbNonZero() - ? mode != TypeSystem::BoolCast::Disabled : mode == TypeSystem::BoolCast::Enabled) { - const auto func = metaClass->findQtIsNullMethod(); - if (!func.isNull()) - return BoolCastFunction{func, true}; - } - return std::nullopt; -} - -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 {}; -} - void CppGenerator::clearTpFuncs() { + // Functions that should not be registered under a name in PyMethodDef, + // but under a special constant under slots. m_tpFuncs = { {u"__str__"_s, {}}, {u"__str__"_s, {}}, - {reprFunction(), {}}, {u"__iter__"_s, {}}, + {REPR_FUNCTION, {}}, {u"__iter__"_s, {}}, {u"__next__"_s, {}} }; + m_nbFuncs = { {u"__abs__"_s, {}}, {u"__pow__"_s, {} }}; } // Prevent ELF symbol qt_version_tag from being generated into the source @@ -351,7 +255,7 @@ static const char includeQDebug[] = "#endif\n" "#include <QtCore/QDebug>\n"; -static QString chopType(QString s) +QString CppGenerator::chopType(QString s) { if (s.endsWith(u"_Type")) s.chop(5); @@ -463,11 +367,7 @@ static QSet<QString> useIntSet() static bool _shouldInheritInt(const AbstractMetaEnum &cppEnum) { - if (!cppEnum.fullName().startsWith(u"PySide6."_s)) - return true; - // static auto intSet = useIntSet(); - // return intSet.contains(cppEnum.fullName()); - return false; + return !cppEnum.fullName().startsWith(u"PySide6."_s); } static QString BuildEnumFlagInfo(const AbstractMetaEnum &cppEnum) @@ -495,15 +395,15 @@ static QString BuildEnumFlagInfo(const AbstractMetaEnum &cppEnum) static void writePyGetSetDefEntry(TextStream &s, const QString &name, const QString &getFunc, const QString &setFunc) { - s << "{const_cast<char *>(\"" << name << "\"), " << getFunc << ", " + s << "{const_cast<char *>(\"" << mangleName(name) << "\"), " << getFunc << ", " << (setFunc.isEmpty() ? NULL_PTR : setFunc) << ", nullptr, nullptr},\n"; } static bool generateRichComparison(const GeneratorContext &c) { - auto *metaClass = c.metaClass(); + const auto metaClass = c.metaClass(); if (c.forSmartPointer()) { - auto te = qSharedPointerCast<const SmartPointerTypeEntry>(metaClass->typeEntry()); + auto te = std::static_pointer_cast<const SmartPointerTypeEntry>(metaClass->typeEntry()); return te->smartPointerType() == TypeSystem::SmartPointerType::Shared; } @@ -514,17 +414,16 @@ void CppGenerator::generateIncludes(TextStream &s, const GeneratorContext &class const IncludeGroupList &includes, const AbstractMetaClassCList &innerClasses) const { - const AbstractMetaClass *metaClass = classContext.metaClass(); + const auto metaClass = classContext.metaClass(); // write license comment s << licenseComment() << '\n'; const bool normalClass = !classContext.forSmartPointer(); - if (normalClass && !avoidProtectedHack() && !metaClass->isNamespace() - && !metaClass->hasPrivateDestructor()) { - s << "//workaround to access protected functions\n"; - s << "#define protected public\n\n"; - } + // Normally only required for classes for which we want to generate protected API, + // but it needs to be generated into all files to ensure ODR for Unity builds. + if (!avoidProtectedHack()) + s << HeaderGenerator::protectedHackDefine; QByteArrayList cppIncludes{"typeinfo", "iterator", // for containers "cctype", "cstring"}; @@ -540,7 +439,7 @@ void CppGenerator::generateIncludes(TextStream &s, const GeneratorContext &class s << includeQDebug; if (metaClass->hasToStringCapability()) s << "#include <QtCore/QBuffer>\n"; - if (metaClass->isQObject()) { + if (isQObject(metaClass)) { s << "#include <pysideqobject.h>\n" << "#include <pysidesignal.h>\n" << "#include <pysideproperty.h>\n" @@ -548,7 +447,6 @@ void CppGenerator::generateIncludes(TextStream &s, const GeneratorContext &class << "#include <pysidemetafunction.h>\n"; } s << "#include <pysideqenum.h>\n" - << "#include <pysideqflags.h>\n" << "#include <pysideqmetatype.h>\n" << "#include <pysideutils.h>\n" << "#include <feature_select.h>\n" @@ -571,13 +469,16 @@ void CppGenerator::generateIncludes(TextStream &s, const GeneratorContext &class if (!innerClasses.isEmpty()) { s << "\n// inner classes\n"; - for (const AbstractMetaClass *innerClass : innerClasses) { + for (const auto &innerClass : innerClasses) { GeneratorContext innerClassContext = contextForClass(innerClass); s << "#include \"" << HeaderGenerator::headerFileNameForContext(innerClassContext) << "\"\n"; } } + if (avoidProtectedHack()) + s << baseWrapperIncludes(classContext); + for (const auto &g : includes) s << g; @@ -588,35 +489,34 @@ void CppGenerator::generateIncludes(TextStream &s, const GeneratorContext &class s << "#include <" << i << ">\n"; } -static const char openTargetExternC[] = R"( -// Target --------------------------------------------------------- - -extern "C" { -)"; - -static const char closeExternC[] = "} // extern \"C\"\n\n"; - // Write methods definition -static void writePyMethodDefs(TextStream &s, const QString &className, - const QString &methodsDefinitions, bool generateCopy) +void CppGenerator::writePyMethodDefs(TextStream &s, const QString &className, + const QString &methodsDefinitions) { s << "static PyMethodDef " << className << "_methods[] = {\n" << indent - << methodsDefinitions << '\n'; - if (generateCopy) { - s << "{\"__copy__\", reinterpret_cast<PyCFunction>(" << className << "___copy__)" - << ", METH_NOARGS, nullptr},\n"; + << methodsDefinitions << METHOD_DEF_SENTINEL << outdent << "};\n\n"; +} + +void CppGenerator::writeModuleCodeSnips(TextStream &s, const CodeSnipList &codeSnips, + TypeSystem::CodeSnipPosition position, + TypeSystem::Language language) const +{ + if (!codeSnips.isEmpty()) { + try { + writeCodeSnips(s, codeSnips, position, language); + } catch (const std::exception &e) { + throw Exception(msgSnippetError("module source of "_L1 + moduleName(), e.what())); + } } - s << methodDefSentinel << outdent - << "};\n\n"; } -static bool hasHashFunction(const AbstractMetaClass *c) +bool CppGenerator::hasHashFunction(const AbstractMetaClassCPtr &c) { return !c->typeEntry()->hashFunction().isEmpty() || c->hasHashFunction(); } -static bool needsTypeDiscoveryFunction(const AbstractMetaClass *c) +static bool needsTypeDiscoveryFunction(const AbstractMetaClassCPtr &c) { return c->baseClass() != nullptr && (c->isPolymorphic() || !c->typeEntry()->polymorphicIdValue().isEmpty()); @@ -646,7 +546,7 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon } s.setLanguage(TextStream::Language::Cpp); - const AbstractMetaClass *metaClass = classContext.metaClass(); + AbstractMetaClassCPtr metaClass = classContext.metaClass(); const auto typeEntry = metaClass->typeEntry(); auto innerClasses = metaClass->innerClasses(); @@ -671,10 +571,10 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon // Use class base namespace { - const AbstractMetaClass *context = metaClass->enclosingClass(); + AbstractMetaClassCPtr context = metaClass->enclosingClass(); while (context) { if (context->isNamespace() && !context->enclosingClass() - && qSharedPointerCast<const NamespaceTypeEntry>(context->typeEntry())->generateUsing()) { + && std::static_pointer_cast<const NamespaceTypeEntry>(context->typeEntry())->generateUsing()) { s << "\nusing namespace " << context->qualifiedCppName() << ";\n"; break; } @@ -682,7 +582,7 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon } } - s << '\n' << typeNameFunc << '\n'; + s << '\n'; // class inject-code native/beginning if (!typeEntry->codeSnips().isEmpty()) { @@ -694,7 +594,7 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon // python conversion rules if (typeEntry->isValue()) { - auto vte = qSharedPointerCast<const ValueTypeEntry>(typeEntry); + auto vte = std::static_pointer_cast<const ValueTypeEntry>(typeEntry); if (vte->hasTargetConversionRule()) { s << "// Python Conversion\n"; s << vte->targetConversionRule() << '\n'; @@ -721,13 +621,15 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon writeVirtualMethodNative(s, func, maxOverrides++); } - if (!avoidProtectedHack() || !metaClass->hasPrivateDestructor()) { - if (usePySideExtensions() && metaClass->isQObject()) - writeMetaObjectMethod(s, classContext); + if (shouldGenerateMetaObjectFunctions(metaClass)) + writeMetaObjectMethod(s, classContext); + if (!avoidProtectedHack() || !metaClass->hasPrivateDestructor()) writeDestructorNative(s, classContext); - } } + for (const auto &f : metaClass->userAddedPythonOverrides()) + writeUserAddedPythonOverride(s, f); + StringStream smd(TextStream::Language::Cpp); StringStream md(TextStream::Language::Cpp); StringStream signatureStream(TextStream::Language::Cpp); @@ -767,22 +669,22 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon smd << "static PyMethodDef " << methDefName << " = " << indent << defEntries.constFirst() << outdent << ";\n\n"; } - if (!m_tpFuncs.contains(rfunc->name())) + const auto &fname = rfunc->name(); + if (!m_tpFuncs.contains(fname) && !m_nbFuncs.contains(fname)) md << defEntries; } } for (const auto &pyMethodDef : typeEntry->addedPyMethodDefEntrys()) md << pyMethodDef << ",\n"; + + if (typeEntry->isValue()) + writeCopyFunction(s, md, signatureStream, classContext); + const QString methodsDefinitions = md.toString(); const QString singleMethodDefinitions = smd.toString(); const QString className = chopType(cpythonTypeName(metaClass)); - if (typeEntry->isValue()) { - writeCopyFunction(s, classContext); - signatureStream << fullPythonClassName(metaClass) << ".__copy__()\n"; - } - // Write single method definitions s << singleMethodDefinitions; @@ -818,7 +720,7 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon } // Write methods definition - writePyMethodDefs(s, className, methodsDefinitions, typeEntry->isValue()); + writePyMethodDefs(s, className, methodsDefinitions); // Write tp_s/getattro function const AttroCheck attroCheck = checkAttroFunctionNeeds(metaClass); @@ -831,25 +733,8 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon writeNbBoolFunction(classContext, f.value(), s); if (supportsNumberProtocol(metaClass)) { - const QList<AbstractMetaFunctionCList> opOverloads = filterGroupedOperatorFunctions( - metaClass, - OperatorQueryOption::ArithmeticOp - | OperatorQueryOption::IncDecrementOp - | OperatorQueryOption::LogicalOp - | OperatorQueryOption::BitwiseOp); - - for (const AbstractMetaFunctionCList &allOverloads : opOverloads) { - AbstractMetaFunctionCList overloads; - for (const auto &func : allOverloads) { - if (!func->isModifiedRemoved() - && !func->isPrivate() - && (func->ownerClass() == func->implementingClass() || func->isAbstract())) - overloads.append(func); - } - - if (overloads.isEmpty()) - continue; - + const auto numberProtocolOps = numberProtocolOperators(metaClass); + for (const auto &overloads : numberProtocolOps) { OverloadData overloadData(overloads, api()); writeMethodWrapper(s, overloadData, classContext); writeSignatureInfo(signatureStream, overloadData); @@ -928,11 +813,10 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon writeClassDefinition(s, metaClass, classContext); s << '\n'; - if (needsTypeDiscoveryFunction(metaClass)) + if (needsTypeDiscoveryFunction(metaClass)) { writeTypeDiscoveryFunction(s, metaClass); - - writeFlagsNumberMethodsDefinitions(s, classEnums); - s << '\n'; + s << '\n'; + } writeConverterFunctions(s, metaClass, classContext); writeAddedTypeSignatures(signatureStream, typeEntry); @@ -950,147 +834,6 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon } } -static bool hasParameterPredicate(const AbstractMetaFunctionCPtr &f) -{ - return !f->arguments().isEmpty(); -} - -void CppGenerator::generateSmartPointerClass(TextStream &s, const GeneratorContext &classContext) -{ - s.setLanguage(TextStream::Language::Cpp); - const AbstractMetaClass *metaClass = classContext.metaClass(); - const auto typeEntry = qSharedPointerCast<const SmartPointerTypeEntry>(metaClass->typeEntry()); - const bool hasPointeeClass = classContext.pointeeClass() != nullptr; - const auto smartPointerType = typeEntry->smartPointerType(); - const bool isValueHandle = smartPointerType ==TypeSystem::SmartPointerType::ValueHandle; - - IncludeGroup includes{u"Extra includes"_s, typeEntry->extraIncludes()}; - if (hasPointeeClass) - includes.append(classContext.pointeeClass()->typeEntry()->include()); - generateIncludes(s, classContext, {includes}); - - s << '\n' << typeNameFunc << '\n'; - - // Create string literal for smart pointer getter method. - QString rawGetter = typeEntry->getter(); - s << "static const char " << SMART_POINTER_GETTER << "[] = \"" << rawGetter << "\";"; - - // class inject-code native/beginning - if (!typeEntry->codeSnips().isEmpty()) { - writeClassCodeSnips(s, typeEntry->codeSnips(), - TypeSystem::CodeSnipPositionBeginning, TypeSystem::NativeCode, - classContext); - s << '\n'; - } - - StringStream smd(TextStream::Language::Cpp); - StringStream md(TextStream::Language::Cpp); - StringStream signatureStream(TextStream::Language::Cpp); - - s << openTargetExternC; - - const auto &functionGroups = getFunctionGroups(metaClass); - - // Skip all public methods of the smart pointer except for the special - // methods declared in the type entry. - - auto ctors = metaClass->queryFunctions(FunctionQueryOption::Constructors); - if (!hasPointeeClass && !isValueHandle) { // Cannot generate "int*" - auto end = std::remove_if(ctors.begin(), ctors.end(), hasParameterPredicate); - ctors.erase(end, ctors.end()); - } - - if (!ctors.isEmpty()) { - OverloadData overloadData(ctors, api()); - writeConstructorWrapper(s, overloadData, classContext); - writeSignatureInfo(signatureStream, overloadData); - } - - if (!typeEntry->resetMethod().isEmpty()) { - auto it = functionGroups.constFind(typeEntry->resetMethod()); - if (it == functionGroups.cend()) - throw Exception(msgCannotFindSmartPointerMethod(typeEntry, typeEntry->resetMethod())); - AbstractMetaFunctionCList resets = it.value(); - if (!hasPointeeClass && !isValueHandle) { // Cannot generate "int*" - auto end = std::remove_if(resets.begin(), resets.end(), hasParameterPredicate); - resets.erase(end, resets.end()); - } - if (!resets.isEmpty()) - writeMethodWrapper(s, md, signatureStream, resets, classContext); - } - - auto it = functionGroups.constFind(rawGetter); - if (it == functionGroups.cend() || it.value().size() != 1) - throw Exception(msgCannotFindSmartPointerGetter(typeEntry)); - - writeMethodWrapper(s, md, signatureStream, it.value(), classContext); - - QStringList optionalMethods; - if (!typeEntry->refCountMethodName().isEmpty()) - optionalMethods.append(typeEntry->refCountMethodName()); - const QString valueCheckMethod = typeEntry->valueCheckMethod(); - if (!valueCheckMethod.isEmpty() && !valueCheckMethod.startsWith(u"operator")) - optionalMethods.append(valueCheckMethod); - if (!typeEntry->nullCheckMethod().isEmpty()) - optionalMethods.append(typeEntry->nullCheckMethod()); - - for (const QString &optionalMethod : optionalMethods) { - auto it = functionGroups.constFind(optionalMethod); - if (it == functionGroups.cend() || it.value().size() != 1) - throw Exception(msgCannotFindSmartPointerMethod(typeEntry, optionalMethod)); - writeMethodWrapper(s, md, signatureStream, it.value(), classContext); - } - - const QString methodsDefinitions = md.toString(); - const QString singleMethodDefinitions = smd.toString(); - - const QString className = chopType(cpythonTypeName(typeEntry)); - - writeCopyFunction(s, classContext); - signatureStream << fullPythonClassName(metaClass) << ".__copy__()\n"; - - // Write single method definitions - s << singleMethodDefinitions; - - // Write methods definition - writePyMethodDefs(s, className, methodsDefinitions, true /* ___copy__ */); - - // Write tp_s/getattro function - const auto boolCastOpt = boolCast(metaClass); - writeSmartPointerGetattroFunction(s, classContext, boolCastOpt); - writeSmartPointerSetattroFunction(s, classContext); - - if (boolCastOpt.has_value()) - writeNbBoolFunction(classContext, boolCastOpt.value(), s); - - if (smartPointerType == TypeSystem::SmartPointerType::Shared) - writeSmartPointerRichCompareFunction(s, classContext); - - s << closeExternC; - - if (hasHashFunction(metaClass)) - writeHashFunction(s, classContext); - - // Write tp_traverse and tp_clear functions. - writeTpTraverseFunction(s, metaClass); - writeTpClearFunction(s, metaClass); - - writeClassDefinition(s, metaClass, classContext); - - s << '\n'; - - writeConverterFunctions(s, metaClass, classContext); - writeClassRegister(s, metaClass, classContext, signatureStream); - - // class inject-code native/end - if (!typeEntry->codeSnips().isEmpty()) { - writeClassCodeSnips(s, typeEntry->codeSnips(), - TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode, - classContext); - s << '\n'; - } -} - void CppGenerator::writeMethodWrapper(TextStream &s, TextStream &definitionStream, TextStream &signatureStream, const AbstractMetaFunctionCList &overloads, @@ -1132,7 +875,7 @@ void CppGenerator::writeConstructorNative(TextStream &s, const GeneratorContext } void CppGenerator::writeDestructorNative(TextStream &s, - const GeneratorContext &classContext) const + const GeneratorContext &classContext) { s << classContext.wrapperName() << "::~" << classContext.wrapperName() << "()\n{\n" << indent; @@ -1157,9 +900,10 @@ QString CppGenerator::getVirtualFunctionReturnTypeName(const AbstractMetaFunctio // SbkType would return null when the type is a container. auto typeEntry = func->type().typeEntry(); if (typeEntry->isContainer()) { - const auto cte = qSharedPointerCast<const ContainerTypeEntry>(typeEntry); + const auto cte = std::static_pointer_cast<const ContainerTypeEntry>(typeEntry); switch (cte->containerKind()) { case ContainerTypeEntry::ListContainer: + case ContainerTypeEntry::SpanContainer: break; case ContainerTypeEntry::SetContainer: return uR"("set")"_s; @@ -1186,8 +930,8 @@ QString CppGenerator::getVirtualFunctionReturnTypeName(const AbstractMetaFunctio if (func->type().isPrimitive()) return u'"' + func->type().name() + u'"'; - return u"reinterpret_cast<PyTypeObject *>(Shiboken::SbkType< "_s - + typeEntry->qualifiedCppName() + u" >())->tp_name"_s; + return u"Shiboken::SbkType< "_s + + typeEntry->qualifiedCppName() + u" >()->tp_name"_s; } // When writing an overridden method of a wrapper class, write the part @@ -1232,17 +976,24 @@ void CppGenerator::writeVirtualMethodCppCall(TextStream &s, } // Determine the return statement (void or a result value). -QString CppGenerator::virtualMethodReturn(TextStream &s, const ApiExtractorResult &api, - const AbstractMetaFunctionCPtr &func, - const FunctionModificationList &functionModifications) + +CppGenerator::VirtualMethodReturn + CppGenerator::virtualMethodReturn(const ApiExtractorResult &api, + const AbstractMetaFunctionCPtr &func, + const FunctionModificationList &functionModifications) { - if (func->isVoid()) - return u"return;"_s; + VirtualMethodReturn result; + if (func->isVoid()) { + result.statement = "return;"_L1; + return result; + } + + result.statement = "return "_L1; const AbstractMetaType &returnType = func->type(); for (const FunctionModification &mod : functionModifications) { for (const ArgumentModification &argMod : mod.argument_mods()) { if (argMod.index() == 0 && !argMod.replacedDefaultExpression().isEmpty()) { - static const QRegularExpression regex(QStringLiteral("%(\\d+)")); + static const QRegularExpression regex("%(\\d+)"_L1); Q_ASSERT(regex.isValid()); QString expr = argMod.replacedDefaultExpression(); for (int offset = 0; ; ) { @@ -1258,8 +1009,8 @@ QString CppGenerator::virtualMethodReturn(TextStream &s, const ApiExtractorResul offset = match.capturedStart(1); } DefaultValue defaultReturnExpr(DefaultValue::Custom, expr); - return u"return "_s + defaultReturnExpr.returnValue() - + u';'; + result.statement += defaultReturnExpr.returnValue() + u';'; + return result; } } } @@ -1271,22 +1022,19 @@ QString CppGenerator::virtualMethodReturn(TextStream &s, const ApiExtractorResul errorMsg = msgCouldNotFindMinimalConstructor(errorMsg, func->type().cppSignature(), errorMessage); - qCWarning(lcShiboken).noquote().nospace() << errorMsg; - s << "\n#error " << errorMsg << '\n'; - } - if (returnType.referenceType() == LValueReference) { - s << "static " << returnType.typeEntry()->qualifiedCppName() - << " result;\n"; - return u"return result;"_s; + throw Exception(errorMsg); } - return u"return "_s + defaultReturnExpr->returnValue() - + u';'; + + result.needsReference = returnType.referenceType() == LValueReference; + result.statement += (result.needsReference + ? virtualMethodStaticReturnVar : defaultReturnExpr->returnValue()) + u';'; + return result; } // Create an argument for Py_BuildValue() when writing virtual methods. // Return a pair of (argument, format-char). -QPair<QString, QChar> CppGenerator::virtualMethodNativeArg(const AbstractMetaFunctionCPtr &func, - const AbstractMetaArgument &arg) +std::pair<QString, QChar> CppGenerator::virtualMethodNativeArg(const AbstractMetaFunctionCPtr &func, + const AbstractMetaArgument &arg) { if (func->hasConversionRule(TypeSystem::TargetLangCode, arg.argumentIndex() + 1)) return {arg.name() + CONV_RULE_OUT_VAR_SUFFIX, u'N'}; @@ -1312,7 +1060,7 @@ static const char PYTHON_ARGS_ARRAY[] = "pyArgArray"; void CppGenerator::writeVirtualMethodNativeVectorCallArgs(TextStream &s, const AbstractMetaFunctionCPtr &func, const AbstractMetaArgumentList &arguments, - const QList<int> &invalidateArgs) const + const QList<int> &invalidateArgs) { Q_ASSERT(!arguments.isEmpty()); s << "PyObject *" << PYTHON_ARGS_ARRAY <<'[' << arguments.size() << "] = {\n" << indent; @@ -1333,15 +1081,15 @@ void CppGenerator::writeVirtualMethodNativeVectorCallArgs(TextStream &s, if (!invalidateArgs.isEmpty()) s << '\n'; for (int index : invalidateArgs) { - s << "const bool invalidateArg" << index << " = " << PYTHON_ARGS_ARRAY << - '[' << index - 1 << "]->ob_refcnt == 1;\n"; + s << "const bool invalidateArg" << index << " = Py_REFCNT(" << PYTHON_ARGS_ARRAY << + '[' << index - 1 << "]) == 1;\n"; } } void CppGenerator::writeVirtualMethodNativeArgs(TextStream &s, const AbstractMetaFunctionCPtr &func, const AbstractMetaArgumentList &arguments, - const QList<int> &invalidateArgs) const + const QList<int> &invalidateArgs) { s << "Shiboken::AutoDecRef " << PYTHON_ARGS << '('; if (arguments.isEmpty()) { @@ -1361,8 +1109,8 @@ void CppGenerator::writeVirtualMethodNativeArgs(TextStream &s, << indent << argConversions.join(u",\n"_s) << outdent << "\n));\n"; for (int index : std::as_const(invalidateArgs)) { - s << "bool invalidateArg" << index << " = PyTuple_GET_ITEM(" << PYTHON_ARGS - << ", " << index - 1 << ")->ob_refcnt == 1;\n"; + s << "bool invalidateArg" << index << " = Py_REFCNT(PyTuple_GET_ITEM(" << PYTHON_ARGS + << ", " << index - 1 << ")) == 1;\n"; } } @@ -1373,13 +1121,42 @@ static bool isArgumentNotRemoved(const AbstractMetaArgument &a) // PyObject_Vectorcall(): since 3.9 static const char vectorCallCondition[] = - "#if !defined(Py_LIMITED_API) && PY_VERSION_HEX >= 0x03090000\n"; + "#if !defined(PYPY_VERSION) && !defined(Py_LIMITED_API)\n"; // PyObject_CallNoArgs(): since 3.9, stable API since 3.10 static const char noArgsCallCondition[] = - "#if (defined(Py_LIMITED_API) && Py_LIMITED_API >= 0x030A0000) || (!defined(Py_LIMITED_API) && PY_VERSION_HEX >= 0x03090000)\n"; + "#if !defined(PYPY_VERSION) && ((defined(Py_LIMITED_API) && Py_LIMITED_API >= 0x030A0000) || !defined(Py_LIMITED_API))\n"; static const char inverseNoArgsCallCondition[] = - "#if (defined(Py_LIMITED_API) && Py_LIMITED_API < 0x030A0000) || (!defined(Py_LIMITED_API) && PY_VERSION_HEX < 0x03090000)\n"; + "#if defined(PYPY_VERSION) || (defined(Py_LIMITED_API) && Py_LIMITED_API < 0x030A0000)\n"; + +static inline void writeVirtualMethodStaticReturnVar(TextStream &s, const AbstractMetaFunctionCPtr &func) +{ + s << "static " << func->type().typeEntry()->qualifiedCppName() << ' ' + << virtualMethodStaticReturnVar << ";\n"; +} + +static void writeFuncNameVar(TextStream &s, const AbstractMetaFunctionCPtr &func, + const QString &funcName) +{ + // PYSIDE-1019: Add info about properties + int propFlag = 0; + if (func->isPropertyReader()) + propFlag |= 1; + if (func->isPropertyWriter()) + propFlag |= 2; + if (propFlag && func->isStatic()) + propFlag |= 4; + QString propStr; + if (propFlag != 90) + propStr = QString::number(propFlag) + u':'; + + if (propFlag != 0) + s << "// This method belongs to a property.\n"; + s << "static const char *funcName = \""; + if (propFlag != 0) + s << propFlag << ':'; + s << funcName << "\";\n"; +} void CppGenerator::writeVirtualMethodNative(TextStream &s, const AbstractMetaFunctionCPtr &func, @@ -1394,13 +1171,16 @@ void CppGenerator::writeVirtualMethodNative(TextStream &s, Generator::OriginalTypeDescription) << "\n{\n" << indent; - const QString returnStatement = virtualMethodReturn(s, api(), func, - func->modifications()); + const auto returnStatement = virtualMethodReturn(api(), func, + func->modifications()); + + if (returnStatement.needsReference) + writeVirtualMethodStaticReturnVar(s, func); const bool isAbstract = func->isAbstract(); if (isAbstract && func->isModifiedRemoved()) { - qCWarning(lcShiboken, "%s", qPrintable(msgPureVirtualFunctionRemoved(func.data()))); - s << returnStatement << '\n' << outdent << "}\n\n"; + qCWarning(lcShiboken, "%s", qPrintable(msgPureVirtualFunctionRemoved(func.get()))); + s << returnStatement.statement << '\n' << outdent << "}\n\n"; return; } @@ -1429,7 +1209,7 @@ void CppGenerator::writeVirtualMethodNative(TextStream &s, s << "if (m_PyMethodCache[" << cacheIndex << "])" << (multi_line ? " {\n" : "\n") << indent; writeVirtualMethodCppCall(s, func, funcName, snips, lastArg, retType, - returnStatement, false); + returnStatement.statement, false); s << outdent; if (multi_line) s << "}\n"; @@ -1437,34 +1217,33 @@ void CppGenerator::writeVirtualMethodNative(TextStream &s, s << "Shiboken::GilState gil;\n"; // Get out of virtual method call if someone already threw an error. - s << "if (PyErr_Occurred())\n" << indent - << returnStatement << '\n' << outdent; - - // PYSIDE-1019: Add info about properties - int propFlag = 0; - if (func->isPropertyReader()) - propFlag |= 1; - if (func->isPropertyWriter()) - propFlag |= 2; - if (propFlag && func->isStatic()) - propFlag |= 4; - QString propStr; - if (propFlag) - propStr = QString::number(propFlag) + u':'; + s << "if (" << shibokenErrorsOccurred << ")\n" << indent + << returnStatement.statement << '\n' << outdent; s << "static PyObject *nameCache[2] = {};\n"; - if (propFlag) - s << "// This method belongs to a property.\n"; - s << "static const char *funcName = \"" << propStr << funcName << "\";\n" - << "Shiboken::AutoDecRef " << PYTHON_OVERRIDE_VAR + writeFuncNameVar(s, func, funcName); + s << "Shiboken::AutoDecRef " << PYTHON_OVERRIDE_VAR << "(Shiboken::BindingManager::instance().getOverride(this, nameCache, funcName));\n" << "if (" << PYTHON_OVERRIDE_VAR << ".isNull()) {\n" << indent; if (useOverrideCaching(func->ownerClass())) s << "m_PyMethodCache[" << cacheIndex << "] = true;\n"; writeVirtualMethodCppCall(s, func, funcName, snips, lastArg, retType, - returnStatement, true); + returnStatement.statement, true); s << outdent << "}\n\n"; //WS + if (!snips.isEmpty()) { + writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionPyOverride, + TypeSystem::ShellCode, func, false, lastArg); + } + + writeVirtualMethodPythonOverride(s, func, snips, returnStatement); +} + +void CppGenerator::writeVirtualMethodPythonOverride(TextStream &s, + const AbstractMetaFunctionCPtr &func, + const CodeSnipList &snips, + const VirtualMethodReturn &returnStatement) const +{ writeConversionRule(s, func, TypeSystem::TargetLangCode, false); bool invalidateReturn = false; @@ -1487,7 +1266,7 @@ void CppGenerator::writeVirtualMethodNative(TextStream &s, auto arguments = func->arguments(); auto removedEnd = std::stable_partition(arguments.begin(), arguments.end(), isArgumentNotRemoved); - if (isAbstract) { // Base function is not called, indicate unused arguments. + if (func->isAbstract()) { // Base function is not called, indicate unused arguments. for (auto it = removedEnd; it != arguments.end(); ++it) s << sbkUnusedVariableCast(it->name()); } @@ -1533,7 +1312,7 @@ void CppGenerator::writeVirtualMethodNative(TextStream &s, if (argCount > 0) { s << "PyObject_Vectorcall(" << PYTHON_OVERRIDE_VAR << ", " << PYTHON_ARGS_ARRAY << ", " << argCount << ", nullptr));\n"; - for (int argIndex : qAsConst(invalidateArgs)) { + for (int argIndex : std::as_const(invalidateArgs)) { s << "if (invalidateArg" << argIndex << ")\n" << indent << "Shiboken::Object::invalidate(" << PYTHON_ARGS_ARRAY << '[' << (argIndex - 1) << "]);\n" << outdent; @@ -1558,12 +1337,12 @@ void CppGenerator::writeVirtualMethodNative(TextStream &s, s << "if (" << PYTHON_RETURN_VAR << ".isNull()) {\n" << indent << "// An error happened in python code!\n" - << "PyErr_Print();\n" - << returnStatement << "\n" << outdent + << "Shiboken::Errors::storeErrorOrPrint();\n" + << returnStatement.statement << "\n" << outdent << "}\n"; if (invalidateReturn) { - s << "bool invalidateArg0 = " << PYTHON_RETURN_VAR << "->ob_refcnt == 1;\n" + s << "bool invalidateArg0 = Py_REFCNT(" << PYTHON_RETURN_VAR << ") == 1;\n" << "if (invalidateArg0)\n" << indent << "Shiboken::Object::releaseOwnership(" << PYTHON_RETURN_VAR << ".object());\n" << outdent; @@ -1571,7 +1350,7 @@ void CppGenerator::writeVirtualMethodNative(TextStream &s, if (!func->isVoid()) { - if (func->modifiedTypeName() != cPyObjectT()) { + if (func->modifiedTypeName() != cPyObjectT) { s << "// Check return type\n"; @@ -1583,10 +1362,10 @@ void CppGenerator::writeVirtualMethodNative(TextStream &s, << PYTHON_RETURN_VAR << ");\n" << outdent << "if (!" << PYTHON_TO_CPP_VAR << ") {\n" << indent << "Shiboken::Warnings::warnInvalidReturnValue(\"" - << func->ownerClass()->name() << "\", \"" << funcName << "\", " + << func->ownerClass()->name() << "\", funcName, " << getVirtualFunctionReturnTypeName(func) << ", " << "Py_TYPE(" << PYTHON_RETURN_VAR << ")->tp_name);\n" - << returnStatement << '\n' << outdent + << returnStatement.statement << '\n' << outdent << "}\n"; } else { @@ -1605,10 +1384,10 @@ void CppGenerator::writeVirtualMethodNative(TextStream &s, s << " && " << PYTHON_RETURN_VAR << " != Py_None"; s << ") {\n" << indent << "Shiboken::Warnings::warnInvalidReturnValue(\"" - << func->ownerClass()->name() << "\", \"" << funcName << "\", " + << func->ownerClass()->name() << "\", funcName, " << getVirtualFunctionReturnTypeName(func) << ", " << "Py_TYPE(" << PYTHON_RETURN_VAR << ")->tp_name);\n" - << returnStatement << '\n' << outdent + << returnStatement.statement << '\n' << outdent << "}\n"; } @@ -1645,13 +1424,14 @@ void CppGenerator::writeVirtualMethodNative(TextStream &s, if (!func->isVoid()) { s << "return "; + TypeEntryCPtr retType = func->type().typeEntry(); if (avoidProtectedHack() && retType->isEnum()) { auto metaEnum = api().findAbstractMetaEnum(retType); bool isProtectedEnum = metaEnum.has_value() && metaEnum->isProtected(); if (isProtectedEnum) { QString typeCast; if (metaEnum->enclosingClass()) - typeCast += u"::"_s + metaEnum->enclosingClass()->qualifiedCppName(); + typeCast += getFullTypeName(metaEnum->enclosingClass()); typeCast += u"::"_s + metaEnum->name(); s << '(' << typeCast << ')'; } @@ -1665,6 +1445,28 @@ void CppGenerator::writeVirtualMethodNative(TextStream &s, s << outdent << "}\n\n"; } +void CppGenerator::writeUserAddedPythonOverride(TextStream &s, + const AbstractMetaFunctionCPtr &func) const +{ + TypeEntryCPtr retType = func->type().typeEntry(); + const QString funcName = func->isOperatorOverload() + ? pythonOperatorFunctionName(func) : func->definitionNames().constFirst(); + + const CodeSnipList snips = func->hasInjectedCode() + ? func->injectedCodeSnips() : CodeSnipList(); + + QString prefix = wrapperName(func->ownerClass()) + u"::"_s; + s << '\n' << functionSignature(func, prefix, QString(), Generator::SkipDefaultValues | + Generator::OriginalTypeDescription) + << "\n{\n" << indent << sbkUnusedVariableCast("gil"); + + writeFuncNameVar(s, func, funcName); + + const auto returnStatement = virtualMethodReturn(api(), func, + func->modifications()); + writeVirtualMethodPythonOverride(s, func, snips, returnStatement); +} + void CppGenerator::writeMetaObjectMethod(TextStream &s, const GeneratorContext &classContext) const { @@ -1672,7 +1474,7 @@ void CppGenerator::writeMetaObjectMethod(TextStream &s, const QString wrapperClassName = classContext.wrapperName(); const QString qualifiedCppName = classContext.metaClass()->qualifiedCppName(); s << "const QMetaObject *" << wrapperClassName << "::metaObject() const\n{\n"; - s << indent << "if (QObject::d_ptr->metaObject)\n" + s << indent << "if (QObject::d_ptr->metaObject != nullptr)\n" << indent << "return QObject::d_ptr->dynamicMetaObject();\n" << outdent << "SbkObject *pySelf = Shiboken::BindingManager::instance().retrieveWrapper(this);\n" << "if (pySelf == nullptr)\n" @@ -1715,68 +1517,15 @@ void CppGenerator::writeMetaCast(TextStream &s, const QString wrapperClassName = classContext.wrapperName(); const QString qualifiedCppName = classContext.metaClass()->qualifiedCppName(); s << "void *" << wrapperClassName << "::qt_metacast(const char *_clname)\n{\n" - << indent << "if (!_clname)\n" << indent << "return {};\n" << outdent + << indent << "if (_clname == nullptr)\n" << indent << "return {};\n" << outdent << "SbkObject *pySelf = Shiboken::BindingManager::instance().retrieveWrapper(this);\n" - << "if (pySelf && PySide::inherits(Py_TYPE(pySelf), _clname))\n" + << "if (pySelf != nullptr && PySide::inherits(Py_TYPE(pySelf), _clname))\n" << indent << "return static_cast<void *>(const_cast< " << wrapperClassName << " *>(this));\n" << outdent << "return " << qualifiedCppName << "::qt_metacast(_clname);\n" << outdent << "}\n\n"; } -void CppGenerator::writeFlagsConverterFunctions(TextStream &s, - const FlagsTypeEntryCPtr &flagsType, - const QString &enumTypeName, - const QString &flagsCppTypeName, - const QString &enumTypeCheck) const -{ - Q_ASSERT(flagsType); - const QString flagsTypeName = fixedCppTypeName(flagsType); - const QString flagsPythonType = cpythonTypeNameExt(flagsType); - - StringStream c(TextStream::Language::Cpp); - c << "*reinterpret_cast<" << flagsCppTypeName << " *>(cppOut) =\n" - << " " << flagsCppTypeName - << "(QFlag(int(PySide::QFlags::getValue(reinterpret_cast<PySideQFlagsObject *>(pyIn)))))" - << ";\n"; - writePythonToCppFunction(s, c.toString(), flagsTypeName, flagsTypeName); - - QString pyTypeCheck = u"PyObject_TypeCheck(pyIn, "_s + flagsPythonType + u')'; - writeIsPythonConvertibleToCppFunction(s, flagsTypeName, flagsTypeName, pyTypeCheck); - - c.clear(); - - c << "const int castCppIn = int(*reinterpret_cast<const " - << flagsCppTypeName << " *>(cppIn));\n" << "return " - << "reinterpret_cast<PyObject *>(PySide::QFlags::newObject(castCppIn, " - << flagsPythonType << "));\n"; - writeCppToPythonFunction(s, c.toString(), flagsTypeName, flagsTypeName); - s << '\n'; - - c.clear(); - c << "*reinterpret_cast<" << flagsCppTypeName << " *>(cppOut) =\n" - << " " << flagsCppTypeName - << "(QFlag(int(Shiboken::Enum::getValue(pyIn))));\n"; - - writePythonToCppFunction(s, c.toString(), enumTypeName, flagsTypeName); - writeIsPythonConvertibleToCppFunction(s, enumTypeName, flagsTypeName, enumTypeCheck); - - c.clear(); - c << "Shiboken::AutoDecRef pyLong(PyNumber_Long(pyIn));\n" - << "*reinterpret_cast<" << flagsCppTypeName << " *>(cppOut) =\n" - << " " << flagsCppTypeName - << "(QFlag(int(PyLong_AsLong(pyLong.object()))));\n"; - // PYSIDE-898: Include an additional condition to detect if the type of the - // enum corresponds to the object that is being evaluated. - // Using only `PyNumber_Check(...)` is too permissive, - // then we would have been unable to detect the difference between - // a PolarOrientation and Qt::AlignmentFlag, which was the main - // issue of the bug. - const QString numberCondition = u"PyNumber_Check(pyIn) && "_s + enumTypeCheck; - writePythonToCppFunction(s, c.toString(), u"number"_s, flagsTypeName); - writeIsPythonConvertibleToCppFunction(s, u"number"_s, flagsTypeName, numberCondition); -} - static void generateDeprecatedValueWarnings(TextStream &c, const AbstractMetaEnum &metaEnum, bool useSurrogateName) @@ -1796,14 +1545,15 @@ static void generateDeprecatedValueWarnings(TextStream &c, << "\", \"" << v.name() << "\");\nbreak;\n" << outdent; } if (deprecatedValues.size() < metaEnum.values().size()) - c << "default:\n" << indent << "break;\n" << outdent << "}\n"; + c << "default:\n" << indent << "break;\n" << outdent; + c << "}\n"; } void CppGenerator::writeEnumConverterFunctions(TextStream &s, const AbstractMetaEnum &metaEnum) const { if (metaEnum.isPrivate() || metaEnum.isAnonymous()) return; - EnumTypeEntryPtr enumType = metaEnum.typeEntry(); + EnumTypeEntryCPtr enumType = metaEnum.typeEntry(); Q_ASSERT(enumType); QString typeName = fixedCppTypeName(enumType); QString enumPythonType = cpythonTypeNameExt(enumType); @@ -1824,6 +1574,8 @@ void CppGenerator::writeEnumConverterFunctions(TextStream &s, const AbstractMeta generateDeprecatedValueWarnings(c, metaEnum, useSurrogateName); c << "*reinterpret_cast<" << cppTypeName << " *>(cppOut) = value;\n"; + + ConfigurableScope configScope(s, enumType); writePythonToCppFunction(s, c.toString(), typeName, typeName); QString pyTypeCheck = u"PyObject_TypeCheck(pyIn, "_s + enumPythonType + u')'; @@ -1836,16 +1588,36 @@ void CppGenerator::writeEnumConverterFunctions(TextStream &s, const AbstractMeta << "Shiboken::Enum::newItem(" << enumPythonType << ", castCppIn);\n"; writeCppToPythonFunction(s, c.toString(), typeName, typeName); s << '\n'; +} - // QFlags part. - if (auto flags = enumType->flags(); !flags.isNull()) { - const QString flagsCppTypeName = useSurrogateName - ? cppTypeName : getFullTypeName(flags).trimmed(); - writeFlagsConverterFunctions(s, flags, typeName, flagsCppTypeName, pyTypeCheck); +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 AbstractMetaClass *metaClass, +void CppGenerator::writeConverterFunctions(TextStream &s, const AbstractMetaClassCPtr &metaClass, const GeneratorContext &classContext) const { s << "// Type conversion functions.\n\n"; @@ -1886,34 +1658,11 @@ void CppGenerator::writeConverterFunctions(TextStream &s, const AbstractMetaClas // C++ pointer to a Python wrapper, keeping identity. s << "// C++ to Python pointer conversion - tries to find the Python wrapper for the C++ object (keeps object identity).\n"; c.clear(); - if (usePySideExtensions() && metaClass->isQObject()) { + if (usePySideExtensions() && isQObject(metaClass)) { 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 changedTypeName = 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 && Shiboken::ObjectType::hasSpecialCastFunction(sbkType)) { - typeName = typeNameOf(tCppIn); - changedTypeName = true; -} -)" - << "PyObject *result = Shiboken::Object::newObject(" << cpythonType - << R"(, const_cast<void *>(cppIn), false, /* exactType */ changedTypeName, typeName); -if (changedTypeName) - delete [] typeName; -return result;)"; + writePointerToPythonConverter(c, metaClass, typeName, cpythonType); } std::swap(targetTypeName, sourceTypeName); writeCppToPythonFunction(s, c.toString(), sourceTypeName, targetTypeName); @@ -1942,7 +1691,7 @@ return result;)"; c << "auto *source = reinterpret_cast<const " << typeName << " *>(cppIn);\n"; } c << "return Shiboken::Object::newObject(" << cpythonType - << ", new ::" << classContext.effectiveClassName() << '(' + << ", new " << globalScopePrefix(classContext) << classContext.effectiveClassName() << '(' << (isUniquePointer ? "std::move(*source)" : "*source") << "), true, true);"; writeCppToPythonFunction(s, c.toString(), sourceTypeName, targetTypeName); @@ -1952,7 +1701,7 @@ return result;)"; s << "// Python to C++ copy conversion.\n"; sourceTypeName = metaClass->name(); - targetTypeName = sourceTypeName + QStringLiteral("_COPY"); + targetTypeName = sourceTypeName + "_COPY"_L1; c.clear(); QString pyInVariable = u"pyIn"_s; @@ -1961,7 +1710,7 @@ return result;)"; c << '*' << outPtr << " = *" << cpythonWrapperCPtr(typeEntry, pyInVariable) << ';'; } else { - auto ste = qSharedPointerCast<const SmartPointerTypeEntry>(typeEntry); + auto ste = std::static_pointer_cast<const SmartPointerTypeEntry>(typeEntry); const QString resetMethod = ste->resetMethod(); c << "auto *ptr = " << outPtr << ";\n"; c << "if (" << pyInVariable << " == Py_None)\n" << indent; @@ -1999,7 +1748,7 @@ return result;)"; QString toCppConv; QString toCppPreConv; if (conv->isConversionOperator()) { - const AbstractMetaClass *sourceClass = conv->ownerClass(); + const auto sourceClass = conv->ownerClass(); typeCheck = u"PyObject_TypeCheck(pyIn, "_s + cpythonTypeNameExt(sourceClass->typeEntry()) + u')'; toCppConv = u'*' + cpythonWrapperCPtr(sourceClass->typeEntry(), @@ -2033,14 +1782,14 @@ return result;)"; StringStream pc(TextStream::Language::Cpp); pc << getFullTypeNameWithoutModifiers(sourceType) << " cppIn" << minimalConstructorExpression(api(), sourceType) << ";\n"; - writeToCppConversion(pc, sourceType, nullptr, pyInVariable, + writeToCppConversion(pc, sourceType, pyInVariable, u"cppIn"_s); pc << ';'; toCppPreConv = pc.toString(); toCppConv.append(u"cppIn"_s); } else if (!sourceType.isWrapperType()) { StringStream tcc(TextStream::Language::Cpp); - writeToCppConversion(tcc, sourceType, metaClass, pyInVariable, + writeToCppConversion(tcc, sourceType, pyInVariable, u"/*BOZO-1061*/"_s); toCppConv = tcc.toString(); } @@ -2052,7 +1801,7 @@ return result;)"; } if (typeEntry->isValue()) { - auto vte = qSharedPointerCast<const ValueTypeEntry>(typeEntry); + auto vte = std::static_pointer_cast<const ValueTypeEntry>(typeEntry); writeCustomConverterFunctions(s, vte->customConversion()); } } @@ -2060,7 +1809,7 @@ return result;)"; void CppGenerator::writeCustomConverterFunctions(TextStream &s, const CustomConversionPtr &customConversion) const { - if (customConversion.isNull()) + if (!customConversion) return; const TargetToNativeConversions &toCppConversions = customConversion->targetToNativeConversions(); if (toCppConversions.isEmpty()) @@ -2072,7 +1821,7 @@ void CppGenerator::writeCustomConverterFunctions(TextStream &s, s << '\n'; } -void CppGenerator::writeConverterRegister(TextStream &s, const AbstractMetaClass *metaClass, +void CppGenerator::writeConverterRegister(TextStream &s, const AbstractMetaClassCPtr &metaClass, const GeneratorContext &classContext) const { const auto typeEntry = metaClass->typeEntry(); @@ -2096,9 +1845,8 @@ void CppGenerator::writeConverterRegister(TextStream &s, const AbstractMetaClass auto writeConversions = [&s](const QString &signature) { - s << "Shiboken::Conversions::registerConverterName(converter, \"" << signature << "\");\n" - << "Shiboken::Conversions::registerConverterName(converter, \"" << signature << "*\");\n" - << "Shiboken::Conversions::registerConverterName(converter, \"" << signature << "&\");\n"; + s << registerConverterName(signature) << registerConverterName(signature + u'*') + << registerConverterName(signature + u'&'); }; auto writeConversionsForType = [writeConversions](const QString &fullTypeName) @@ -2123,14 +1871,14 @@ void CppGenerator::writeConverterRegister(TextStream &s, const AbstractMetaClass Qt::SkipEmptyParts); while (!lst.isEmpty()) { QString signature = lst.join(u"::"_s); - writeConversions(smartPointerName + u'<' + signature + u" >"_s); + writeConversions(smartPointerName + u'<' + signature + u'>'); lst.removeFirst(); } writeConversionsForType(smartPointerType); } - s << "Shiboken::Conversions::registerConverterName(converter, typeid(::"; + s << "Shiboken::Conversions::registerConverterName(converter, typeid(" << m_gsp; QString qualifiedCppNameInvocation; if (!classContext.forSmartPointer()) qualifiedCppNameInvocation = metaClass->qualifiedCppName(); @@ -2140,17 +1888,15 @@ void CppGenerator::writeConverterRegister(TextStream &s, const AbstractMetaClass s << qualifiedCppNameInvocation << ").name());\n"; if (classContext.useWrapper()) { - s << "Shiboken::Conversions::registerConverterName(converter, typeid(::" + s << "Shiboken::Conversions::registerConverterName(converter, typeid(" << classContext.wrapperName() << ").name());\n"; } - s << '\n'; - if (!typeEntry->isValue() && !typeEntry->isSmartPointer()) return; // Python to C++ copy (value, not pointer neither reference) conversion. - s << "// Add Python to C++ copy (value, not pointer neither reference) conversion to type converter.\n"; + s << "\n// Add Python to C++ copy (value, not pointer neither reference) conversion to type converter.\n"; sourceTypeName = metaClass->name(); targetTypeName = sourceTypeName + u"_COPY"_s; QString toCpp = pythonToCppFunctionName(sourceTypeName, targetTypeName); @@ -2185,7 +1931,7 @@ void CppGenerator::writeConverterRegister(TextStream &s, const AbstractMetaClass } if (typeEntry->isValue()) { - auto vte = qSharedPointerCast<const ValueTypeEntry>(typeEntry); + auto vte = std::static_pointer_cast<const ValueTypeEntry>(typeEntry); writeCustomConverterRegister(s, vte->customConversion(), u"converter"_s); } } @@ -2194,7 +1940,7 @@ void CppGenerator::writeCustomConverterRegister(TextStream &s, const CustomConversionPtr &customConversion, const QString &converterVar) { - if (customConversion.isNull()) + if (!customConversion) return; const TargetToNativeConversions &toCppConversions = customConversion->targetToNativeConversions(); @@ -2215,46 +1961,7 @@ void CppGenerator::writeContainerConverterFunctions(TextStream &s, writePythonToCppConversionFunctions(s, containerType); } -// Helpers to collect all smart pointer pointee base classes -static AbstractMetaClassCList findSmartPointeeBaseClasses(const ApiExtractorResult &api, - const AbstractMetaType &smartPointerType) -{ - AbstractMetaClassCList result; - auto instantiationsTe = smartPointerType.instantiations().at(0).typeEntry(); - auto targetClass = AbstractMetaClass::findClass(api.classes(), instantiationsTe); - if (targetClass != nullptr) - result = targetClass->allTypeSystemAncestors(); - return result; -} - -void CppGenerator::writeSmartPointerConverterFunctions(TextStream &s, - const AbstractMetaType &smartPointerType) const -{ - const auto baseClasses = findSmartPointeeBaseClasses(api(), smartPointerType); - if (baseClasses.isEmpty()) - return; - - auto smartPointerTypeEntry = - qSharedPointerCast<const SmartPointerTypeEntry>(smartPointerType.typeEntry()); - - // TODO: Missing conversion to smart pointer pointer type: - - s << "// Register smartpointer conversion for all derived classes\n"; - for (auto *base : baseClasses) { - auto baseTe = base->typeEntry(); - if (smartPointerTypeEntry->matchesInstantiation(baseTe)) { - if (auto opt = findSmartPointerInstantiation(smartPointerTypeEntry, baseTe)) { - const auto smartTargetType = opt.value(); - s << "// SmartPointer derived class: " - << smartTargetType.cppSignature() << "\n"; - writePythonToCppConversionFunctions(s, smartPointerType, - smartTargetType, {}, {}, {}); - } - } - } -} - -bool CppGenerator::needsArgumentErrorHandling(const OverloadData &overloadData) const +bool CppGenerator::needsArgumentErrorHandling(const OverloadData &overloadData) { if (overloadData.maxArgs() > 0) return true; @@ -2263,15 +1970,16 @@ bool CppGenerator::needsArgumentErrorHandling(const OverloadData &overloadData) return false; auto rfunc = overloadData.referenceFunction(); return rfunc->functionType() == AbstractMetaFunction::ConstructorFunction - && rfunc->ownerClass()->isQObject(); + && isQObject(rfunc->ownerClass()); } -void CppGenerator::writeMethodWrapperPreamble(TextStream &s,const OverloadData &overloadData, +void CppGenerator::writeMethodWrapperPreamble(TextStream &s, + const OverloadData &overloadData, const GeneratorContext &context, - ErrorReturn errorReturn) const + ErrorReturn errorReturn) { const auto rfunc = overloadData.referenceFunction(); - const AbstractMetaClass *ownerClass = rfunc->targetLangOwner(); + const auto ownerClass = rfunc->targetLangOwner(); Q_ASSERT(ownerClass == context.metaClass()); int minArgs = overloadData.minArgs(); int maxArgs = overloadData.maxArgs(); @@ -2281,7 +1989,9 @@ void CppGenerator::writeMethodWrapperPreamble(TextStream &s,const OverloadData & if (rfunc->isConstructor()) { // Check if the right constructor was called. if (!ownerClass->hasPrivateDestructor()) { - s << "if (Shiboken::Object::isUserType(self) && !Shiboken::ObjectType::canCallConstructor(self->ob_type, Shiboken::SbkType< ::"; + s << "if (Shiboken::Object::isUserType(self) && " + << "!Shiboken::ObjectType::canCallConstructor(self->ob_type, Shiboken::SbkType< " + << m_gsp; QString qualifiedCppName; if (!context.forSmartPointer()) qualifiedCppName = ownerClass->qualifiedCppName(); @@ -2291,7 +2001,7 @@ void CppGenerator::writeMethodWrapperPreamble(TextStream &s,const OverloadData & s << qualifiedCppName << " >()))\n" << indent << errorReturn << outdent << '\n'; } // Declare pointer for the underlying C++ object. - s << "::" << context.effectiveClassName() << " *cptr{};\n"; + s << globalScopePrefix(context) << context.effectiveClassName() << " *cptr{};\n"; initPythonArguments = maxArgs > 0; @@ -2311,11 +2021,18 @@ void CppGenerator::writeMethodWrapperPreamble(TextStream &s,const OverloadData & initPythonArguments = minArgs != maxArgs || maxArgs > 1; } - if (needsArgumentErrorHandling(overloadData)) { - s << R"(Shiboken::AutoDecRef errInfo{}; -static const char fullName[] = ")" << fullPythonFunctionName(rfunc, true) - << "\";\nSBK_UNUSED(fullName)\n"; - } + if (needsArgumentErrorHandling(overloadData)) + s << "Shiboken::AutoDecRef errInfo{};\n"; + + s << "static const char fullName[] = \"" << fullPythonFunctionName(rfunc, true) + << "\";\nSBK_UNUSED(fullName)\n" + << "Shiboken::PythonContextMarker pcm;\n"; + // PYSIDE-2335: Mark blocking calls like `exec` or `run` as such. + bool isBlockingFunction = rfunc->name() == u"exec"_s || rfunc->name() == u"exec_"_s + || rfunc->name() == u"run"_s; + if (isBlockingFunction) + s << "pcm.setBlocking();\n"; + if (maxArgs > 0) { s << "int overloadId = -1;\n" << PYTHON_TO_CPPCONVERSION_STRUCT << ' ' << PYTHON_TO_CPP_VAR; @@ -2341,20 +2058,20 @@ void CppGenerator::writeConstructorWrapper(TextStream &s, const OverloadData &ov const ErrorReturn errorReturn = ErrorReturn::MinusOne; const auto rfunc = overloadData.referenceFunction(); - const AbstractMetaClass *metaClass = rfunc->ownerClass(); + const auto metaClass = rfunc->ownerClass(); s << "static int\n"; s << cpythonFunctionName(rfunc) << "(PyObject *self, PyObject *args, PyObject *kwds)\n{\n" << indent; if (overloadData.maxArgs() == 0 || metaClass->isAbstract()) - s << sbkUnusedVariableCast(u"args"_s); - s << sbkUnusedVariableCast(u"kwds"_s); + s << sbkUnusedVariableCast("args"); + s << sbkUnusedVariableCast("kwds"); - const bool needsMetaObject = usePySideExtensions() && metaClass->isQObject(); + const bool needsMetaObject = usePySideExtensions() && isQObject(metaClass); if (needsMetaObject) s << "const QMetaObject *metaObject;\n"; - s << "SbkObject *sbkSelf = reinterpret_cast<SbkObject *>(self);\n"; + s << "auto *sbkSelf = reinterpret_cast<SbkObject *>(self);\n"; if (metaClass->isAbstract() || metaClass->baseClassNames().size() > 1) { s << "PyTypeObject *type = self->ob_type;\n" @@ -2365,11 +2082,11 @@ void CppGenerator::writeConstructorWrapper(TextStream &s, const OverloadData &ov if (metaClass->isAbstract()) { // C++ Wrapper disabled: Abstract C++ class cannot be instantiated. if (metaClass->typeEntry()->typeFlags().testFlag(ComplexTypeEntry::DisableWrapper)) { - s << sbkUnusedVariableCast(u"sbkSelf"_s) - << sbkUnusedVariableCast(u"type"_s) - << sbkUnusedVariableCast(u"myType"_s); + s << sbkUnusedVariableCast("sbkSelf") + << sbkUnusedVariableCast("type") + << sbkUnusedVariableCast("myType"); if (needsMetaObject) - s << sbkUnusedVariableCast(u"metaObject"_s); + s << sbkUnusedVariableCast("metaObject"); s << "Shiboken::Errors::setInstantiateAbstractClassDisabledWrapper(\"" << metaClass->qualifiedCppName() << "\");\n" << errorReturn << outdent << "}\n\n"; @@ -2401,19 +2118,29 @@ void CppGenerator::writeConstructorWrapper(TextStream &s, const OverloadData &ov s << '\n'; if (overloadData.maxArgs() > 0) - writeOverloadedFunctionDecisor(s, overloadData); + writeOverloadedFunctionDecisor(s, overloadData, errorReturn); + + // Handles Python Multiple Inheritance + QString pre = needsMetaObject ? u"bool usesPyMI = "_s : u""_s; + s << "\n// PyMI support\n" + << pre << "Shiboken::callInheritedInit(self, args, kwds, fullName);\n" + << "if (" << shibokenErrorsOccurred << ")\n" + << indent << errorReturn << outdent << "\n"; writeFunctionCalls(s, overloadData, classContext, errorReturn); s << '\n'; const QString typeName = classContext.forSmartPointer() ? classContext.preciseType().cppSignature() : metaClass->qualifiedCppName(); - s << "if (PyErr_Occurred() || !Shiboken::Object::setCppPointer(sbkSelf, Shiboken::SbkType< ::" - << typeName << " >(), cptr)) {\n" + s << "if (" << shibokenErrorsOccurred + << " || !Shiboken::Object::setCppPointer(sbkSelf, Shiboken::SbkType< " + << globalScopePrefix(classContext) << typeName << " >(), cptr)) {\n" << indent << "delete cptr;\n" << errorReturn << outdent << "}\n"; if (overloadData.maxArgs() > 0) - s << "if (!cptr) goto " << cpythonFunctionName(rfunc) << "_TypeError;\n\n"; + s << "if (cptr == nullptr)\n" << indent + << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n\n" + << outdent; s << "Shiboken::Object::setValidCpp(sbkSelf, true);\n"; // If the created C++ object has a C++ wrapper the ownership is assigned to Python @@ -2435,8 +2162,9 @@ void CppGenerator::writeConstructorWrapper(TextStream &s, const OverloadData &ov << "PySide::Signal::updateSourceObject(self);\n" << "metaObject = cptr->metaObject(); // <- init python qt properties\n" << "if (!errInfo.isNull() && PyDict_Check(errInfo.object())) {\n" << indent - << "if (!PySide::fillQtProperties(self, metaObject, errInfo))\n" << indent - << "goto " << cpythonFunctionName(rfunc) << "_TypeError;\n" << outdent << outdent + << "if (!PySide::fillQtProperties(self, metaObject, errInfo, usesPyMI))\n" << indent + << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n" + << outdent << outdent << "};\n"; } @@ -2474,8 +2202,6 @@ void CppGenerator::writeConstructorWrapper(TextStream &s, const OverloadData &ov } s << "\n\nreturn 1;\n"; - if (needsArgumentErrorHandling(overloadData)) - writeErrorSection(s, overloadData, errorReturn); s<< outdent << "}\n\n"; } @@ -2498,9 +2224,9 @@ void CppGenerator::writeMethodWrapper(TextStream &s, const OverloadData &overloa } s << ")\n{\n" << indent; if (rfunc->ownerClass() == nullptr || overloadData.hasStaticFunction()) - s << sbkUnusedVariableCast(u"self"_s); + s << sbkUnusedVariableCast(PYTHON_SELF_VAR); if (hasKwdArgs) - s << sbkUnusedVariableCast(u"kwds"_s); + s << sbkUnusedVariableCast("kwds"); writeMethodWrapperPreamble(s, overloadData, classContext); @@ -2527,7 +2253,8 @@ void CppGenerator::writeMethodWrapper(TextStream &s, const OverloadData &overloa << "PyObject *revOpMethod = PyObject_GetAttr(" << PYTHON_ARG << ", attrName);\n" << "if (revOpMethod && PyCallable_Check(revOpMethod)) {\n" << indent << PYTHON_RETURN_VAR << " = PyObject_CallFunction(revOpMethod, \"O\", self);\n" - << "if (PyErr_Occurred() && (PyErr_ExceptionMatches(PyExc_NotImplementedError)" + << "if (" << shibokenErrorsOccurred + << " && (PyErr_ExceptionMatches(PyExc_NotImplementedError)" << " || PyErr_ExceptionMatches(PyExc_AttributeError))) {\n" << indent << "PyErr_Clear();\n" << "Py_XDECREF(" << PYTHON_RETURN_VAR << ");\n" @@ -2537,14 +2264,14 @@ void CppGenerator::writeMethodWrapper(TextStream &s, const OverloadData &overloa << "Py_XDECREF(revOpMethod);\n\n" << outdent << "}\n\n" << "// Do not enter here if other object has implemented a reverse operator.\n" - << "if (!" << PYTHON_RETURN_VAR << ") {\n" << indent; + << "if (" << PYTHON_RETURN_VAR << " == nullptr) {\n" << indent; if (maxArgs > 0) - writeOverloadedFunctionDecisor(s, overloadData); + writeOverloadedFunctionDecisor(s, overloadData, ErrorReturn::Default); writeFunctionCalls(s, overloadData, classContext, ErrorReturn::Default); s << outdent << '\n' << "} // End of \"if (!" << PYTHON_RETURN_VAR << ")\"\n"; } else { // binary shift operator if (maxArgs > 0) - writeOverloadedFunctionDecisor(s, overloadData); + writeOverloadedFunctionDecisor(s, overloadData, ErrorReturn::Default); writeFunctionCalls(s, overloadData, classContext, ErrorReturn::Default); } @@ -2563,9 +2290,6 @@ void CppGenerator::writeMethodWrapper(TextStream &s, const OverloadData &overloa s << "Py_RETURN_NONE;\n"; } - if (needsArgumentErrorHandling(overloadData)) - writeErrorSection(s, overloadData, ErrorReturn::Default); - s<< outdent << "}\n\n"; } @@ -2573,7 +2297,7 @@ void CppGenerator::writeArgumentsInitializer(TextStream &s, const OverloadData & ErrorReturn errorReturn) { const auto rfunc = overloadData.referenceFunction(); - s << "PyTuple_GET_SIZE(args);\n" << sbkUnusedVariableCast(u"numArgs"_s); + s << "PyTuple_GET_SIZE(args);\n" << sbkUnusedVariableCast("numArgs"); int minArgs = overloadData.minArgs(); int maxArgs = overloadData.maxArgs(); @@ -2602,15 +2326,16 @@ void CppGenerator::writeArgumentsInitializer(TextStream &s, const OverloadData & // Disable argument count checks for QObject constructors to allow for // passing properties as KW args. - auto *owner = rfunc->ownerClass(); - bool isQObjectConstructor = owner != nullptr && owner->isQObject() + const auto owner = rfunc->ownerClass(); + bool isQObjectConstructor = owner && isQObject(owner) && rfunc->functionType() == AbstractMetaFunction::ConstructorFunction; if (usesNamedArguments && !isQObjectConstructor) { s << "errInfo.reset(Shiboken::checkInvalidArgumentCount(numArgs, " << minArgs << ", " << maxArgs << "));\n" << "if (!errInfo.isNull())\n" << indent - << "goto " << cpythonFunctionName(rfunc) << "_TypeError;\n" << outdent; + << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n" + << outdent; } const QList<int> invalidArgsLength = overloadData.invalidArgumentLengths(); @@ -2622,7 +2347,8 @@ void CppGenerator::writeArgumentsInitializer(TextStream &s, const OverloadData & s << "numArgs == " << invalidArgsLength.at(i); } s << ")\n" << indent - << "goto " << cpythonFunctionName(rfunc) << "_TypeError;\n" << outdent; + << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n" + << outdent; } s << '\n'; @@ -2633,7 +2359,7 @@ void CppGenerator::writeArgumentsInitializer(TextStream &s, const OverloadData & funcName = rfunc->name(); QString argsVar = overloadData.hasVarargs() ? u"nonvarargs"_s : u"args"_s; - s << "if (!"; + s << "if ("; if (usesNamedArguments) { s << "PyArg_ParseTuple(" << argsVar << ", \"|" << QByteArray(maxArgs, 'O') << ':' << funcName << '"'; @@ -2643,7 +2369,7 @@ void CppGenerator::writeArgumentsInitializer(TextStream &s, const OverloadData & } for (int i = 0; i < maxArgs; i++) s << ", &(" << PYTHON_ARGS << '[' << i << "])"; - s << "))\n" << indent << errorReturn << outdent << '\n'; + s << ") == 0)\n" << indent << errorReturn << outdent << '\n'; } void CppGenerator::writeCppSelfConversion(TextStream &s, const GeneratorContext &context, @@ -2654,23 +2380,15 @@ void CppGenerator::writeCppSelfConversion(TextStream &s, const GeneratorContext return; } - static const QString pythonSelfVar = u"self"_s; if (useWrapperClass) s << "static_cast<" << className << " *>("; - s << cpythonWrapperCPtr(context.metaClass(), pythonSelfVar); + s << cpythonWrapperCPtr(context.metaClass(), PYTHON_SELF_VAR); if (useWrapperClass) s << ')'; } -void CppGenerator::writeSmartPointerCppSelfConversion(TextStream &s, - const GeneratorContext &context) -{ - Q_ASSERT(context.forSmartPointer()); - s << cpythonWrapperCPtr(context.preciseType(), u"self"_s); -} - -static inline void writeCppSelfVarDef(TextStream &s, - CppGenerator::CppSelfDefinitionFlags flags = {}) +void CppGenerator::writeCppSelfVarDef(TextStream &s, + CppSelfDefinitionFlags flags) { if (flags.testFlag(CppGenerator::CppSelfAsReference)) s << "auto &" << CPP_SELF_VAR << " = *"; @@ -2678,22 +2396,10 @@ static inline void writeCppSelfVarDef(TextStream &s, s << "auto *" << CPP_SELF_VAR << " = "; } -void CppGenerator::writeSmartPointerCppSelfDefinition(TextStream &s, - const GeneratorContext &context, - ErrorReturn errorReturn, - CppSelfDefinitionFlags flags) -{ - Q_ASSERT(context.forSmartPointer()); - writeInvalidPyObjectCheck(s, u"self"_s, errorReturn); - writeCppSelfVarDef(s, flags); - writeSmartPointerCppSelfConversion(s, context); - s << ";\n"; -} - void CppGenerator::writeCppSelfDefinition(TextStream &s, const GeneratorContext &context, ErrorReturn errorReturn, - CppSelfDefinitionFlags flags) const + CppSelfDefinitionFlags flags) { Q_ASSERT(!(flags.testFlag(CppSelfAsReference) && flags.testFlag(HasStaticOverload))); if (context.forSmartPointer()) { @@ -2701,7 +2407,7 @@ void CppGenerator::writeCppSelfDefinition(TextStream &s, return; } - const AbstractMetaClass *metaClass = context.metaClass(); + AbstractMetaClassCPtr metaClass = context.metaClass(); const auto cppWrapper = context.metaClass()->cppWrapper(); // In the Python method, use the wrapper to access the protected // functions. @@ -2709,10 +2415,9 @@ void CppGenerator::writeCppSelfDefinition(TextStream &s, && cppWrapper.testFlag(AbstractMetaClass::CppProtectedHackWrapper); Q_ASSERT(!useWrapperClass || context.useWrapper()); const QString className = useWrapperClass - ? context.wrapperName() - : (u"::"_s + metaClass->qualifiedCppName()); + ? context.wrapperName() : getFullTypeName(metaClass); - writeInvalidPyObjectCheck(s, u"self"_s, errorReturn); + writeInvalidPyObjectCheck(s, PYTHON_SELF_VAR, errorReturn); if (flags.testFlag(CppSelfAsReference)) { writeCppSelfVarDef(s, flags); @@ -2745,7 +2450,7 @@ void CppGenerator::writeCppSelfDefinition(TextStream &s, const AbstractMetaFunctionCPtr &func, const GeneratorContext &context, ErrorReturn errorReturn, - CppSelfDefinitionFlags flags) const + CppSelfDefinitionFlags flags) { if (!func->ownerClass() || func->isConstructor()) return; @@ -2761,24 +2466,32 @@ void CppGenerator::writeCppSelfDefinition(TextStream &s, writeCppSelfDefinition(s, context, errorReturn, flags); } -void CppGenerator::writeErrorSection(TextStream &s, const OverloadData &overloadData, - ErrorReturn errorReturn) +QString CppGenerator::returnErrorWrongArguments(const OverloadData &overloadData, + ErrorReturn errorReturn) { const auto rfunc = overloadData.referenceFunction(); QString argsVar = overloadData.pythonFunctionWrapperUsesListOfArguments() ? u"args"_s : PYTHON_ARG; - s << '\n' << cpythonFunctionName(rfunc) << "_TypeError:\n" << indent - << "Shiboken::setErrorAboutWrongArguments(" << argsVar << ", fullName, errInfo);\n" - << errorReturn << outdent; + switch (errorReturn) { + case ErrorReturn::Default: + return u"Shiboken::returnWrongArguments("_s + argsVar + u", fullName, errInfo)"_s; + case ErrorReturn::Zero: + return u"Shiboken::returnWrongArguments_Zero("_s + argsVar + u", fullName, errInfo)"_s; + case ErrorReturn::MinusOne: + return u"Shiboken::returnWrongArguments_MinusOne("_s + argsVar + u", fullName, errInfo)"_s; + case ErrorReturn::Void: + Q_ASSERT(false); + } + return {}; } void CppGenerator::writeFunctionReturnErrorCheckSection(TextStream &s, ErrorReturn errorReturn, bool hasReturnValue) { - s << "if (PyErr_Occurred()"; + s << "if (" << shibokenErrorsOccurred; if (hasReturnValue) - s << " || !" << PYTHON_RETURN_VAR; + s << " || " << PYTHON_RETURN_VAR << " == nullptr"; s << ") {\n" << indent; if (hasReturnValue) s << "Py_XDECREF(" << PYTHON_RETURN_VAR << ");\n"; @@ -2886,7 +2599,7 @@ static void checkTypeViability(const AbstractMetaFunctionCPtr &func) } void CppGenerator::writeTypeCheck(TextStream &s, - const QSharedPointer<OverloadDataNode> &overloadData, + const std::shared_ptr<OverloadDataNode> &overloadData, const QString &argumentName) { QSet<TypeEntryCPtr> numericTypes; @@ -2918,7 +2631,7 @@ qsizetype CppGenerator::writeArgumentConversion(TextStream &s, const QString &argName, const QString &pyArgName, ErrorReturn errorReturn, - const AbstractMetaClass *context, + const AbstractMetaClassCPtr &context, const QString &defaultValue, bool castArgumentAsUnused) const { @@ -2950,12 +2663,12 @@ static inline QString arrayHandleType(const AbstractMetaTypeList &nestedArrayTyp { switch (nestedArrayTypes.size()) { case 1: - return QStringLiteral("Shiboken::Conversions::ArrayHandle<") + return "Shiboken::Conversions::ArrayHandle<"_L1 + nestedArrayTypes.constLast().minimalSignature() + u'>'; case 2: - return QStringLiteral("Shiboken::Conversions::Array2Handle<") + return "Shiboken::Conversions::Array2Handle<"_L1 + nestedArrayTypes.constLast().minimalSignature() - + QStringLiteral(", ") + + ", "_L1 + QString::number(nestedArrayTypes.constFirst().arrayElementCount()) + u'>'; } @@ -2994,7 +2707,7 @@ qsizetype CppGenerator::writePythonToCppTypeConversion(TextStream &s, const AbstractMetaType &type, const QString &pyIn, const QString &cppOut, - const AbstractMetaClass *context, + const AbstractMetaClassCPtr &context, const QString &defaultValue) const { TypeEntryCPtr typeEntry = type.typeEntry(); @@ -3030,7 +2743,7 @@ qsizetype CppGenerator::writePythonToCppTypeConversion(TextStream &s, // conversion for &cppOut s << ' ' << cppOutAux; // No default value for containers which can also be passed by pointer. - if (arg.type != GeneratorArgument::Type::Container) + if (arg.type != GeneratorArgument::Type::Container || type.indirections() == 0) writeMinimalConstructorExpression(s, api(), type, isPrimitive, defaultValue); s << ";\n" << typeName << " *" << cppOut << " = &" << cppOutAux; } @@ -3062,7 +2775,7 @@ qsizetype CppGenerator::writePythonToCppTypeConversion(TextStream &s, || arg.type == GeneratorArgument::Type::Enum || arg.type == GeneratorArgument::Type::Flags) { writeMinimalConstructorExpression(s, api(), typeEntry, isPrimitive, defaultValue); - } else if (!type.isContainer() && !type.isSmartPointer()) { + } else if ((!type.isContainer() || type.indirections() == 0) && !type.isSmartPointer()) { writeMinimalConstructorExpression(s, api(), type, isPrimitive, defaultValue); } break; @@ -3157,7 +2870,9 @@ void CppGenerator::writeNoneReturn(TextStream &s, const AbstractMetaFunctionCPtr } } -void CppGenerator::writeOverloadedFunctionDecisor(TextStream &s, const OverloadData &overloadData) const +void CppGenerator::writeOverloadedFunctionDecisor(TextStream &s, + const OverloadData &overloadData, + ErrorReturn errorReturn) const { s << "// Overloaded function decisor\n"; const auto rfunc = overloadData.referenceFunction(); @@ -3167,7 +2882,7 @@ void CppGenerator::writeOverloadedFunctionDecisor(TextStream &s, const OverloadD s << "// " << i << ": "; if (func->isStatic()) s << "static "; - if (const auto *decl = func->declaringClass()) + if (const auto &decl = func->declaringClass()) s << decl->name() << "::"; s << func->signatureComment() << '\n'; } @@ -3184,8 +2899,9 @@ void CppGenerator::writeOverloadedFunctionDecisor(TextStream &s, const OverloadD } s << "// Function signature not found.\n" - << "if (overloadId == -1) goto " - << cpythonFunctionName(overloadData.referenceFunction()) << "_TypeError;\n\n"; + << "if (overloadId == -1)\n" << indent + << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n\n" + << outdent; } void CppGenerator::writeOverloadedFunctionDecisorEngine(TextStream &s, @@ -3251,7 +2967,7 @@ void CppGenerator::writeOverloadedFunctionDecisorEngine(TextStream &s, auto func = referenceFunction; for (const auto &child : children) { const auto defValFunc = child->getFunctionWithDefaultValue(); - if (!defValFunc.isNull()) { + if (defValFunc) { func = defValFunc; break; } @@ -3277,7 +2993,7 @@ void CppGenerator::writeOverloadedFunctionDecisorEngine(TextStream &s, int sequenceArgCount = 0; while (od && !od->argType().isVarargs()) { const bool typeReplacedByPyObject = od->isTypeModified() - && od->modifiedArgType().name() == cPyObjectT(); + && od->modifiedArgType().name() == cPyObjectT; if (!typeReplacedByPyObject) { if (usePyArgs) pyArgName = pythonArgsAt(od->argPos()); @@ -3285,7 +3001,7 @@ void CppGenerator::writeOverloadedFunctionDecisorEngine(TextStream &s, auto func = od->referenceFunction(); if (func->isConstructor() && func->arguments().size() == 1) { - const AbstractMetaClass *ownerClass = func->ownerClass(); + AbstractMetaClassCPtr ownerClass = func->ownerClass(); ComplexTypeEntryCPtr baseContainerType = ownerClass->typeEntry()->baseContainerType(); if (baseContainerType && baseContainerType == func->arguments().constFirst().type().typeEntry() && ownerClass->isCopyable()) { @@ -3341,7 +3057,7 @@ void CppGenerator::writeOverloadedFunctionDecisorEngine(TextStream &s, s << indent << typeChecks.join(u"\n&& "_s) << outdent; } s << ") {\n" << indent; - writeOverloadedFunctionDecisorEngine(s, overloadData, child.data()); + writeOverloadedFunctionDecisorEngine(s, overloadData, child.get()); s << outdent << '}'; } s << '\n'; @@ -3374,11 +3090,12 @@ static void writeDeprecationWarning(TextStream &s, CppGenerator::ErrorReturn errorReturn) { s << "Shiboken::Warnings::warnDeprecated(\""; - if (auto *cls = context.metaClass()) + if (const auto cls = context.metaClass()) s << cls->name() << "\", "; // Check error in case "warning-as-error" is set. s << '"' << func->signature().replace(u"::"_s, u"."_s) << "\");\n" - << "if (PyErr_Occurred())\n" << indent << errorReturn << outdent; + << "if (" << shibokenErrorsOccurred << ")\n" + << indent << errorReturn << outdent; } void CppGenerator::writeSingleFunctionCall(TextStream &s, @@ -3400,7 +3117,7 @@ void CppGenerator::writeSingleFunctionCall(TextStream &s, const bool usePyArgs = overloadData.pythonFunctionWrapperUsesListOfArguments(); // Handle named arguments. - writeNamedArgumentResolution(s, func, usePyArgs, overloadData); + writeNamedArgumentResolution(s, func, usePyArgs, overloadData, errorReturn); bool injectCodeCallsFunc = injectedCodeCallsCppFunction(context, func); bool mayHaveUnunsedArguments = !func->isUserAdded() && func->hasInjectedCode() && injectCodeCallsFunc; @@ -3414,8 +3131,7 @@ void CppGenerator::writeSingleFunctionCall(TextStream &s, const AbstractMetaArgument &arg = func->arguments().at(argIdx); if (arg.isModifiedRemoved()) { if (!arg.defaultValueExpression().isEmpty()) { - const QString cppArgRemoved = CPP_ARG_REMOVED - + QString::number(argIdx); + const QString cppArgRemoved = CPP_ARG_REMOVED(argIdx); s << getFullTypeName(arg.type()) << ' ' << cppArgRemoved; s << " = " << arg.defaultValueExpression() << ";\n" << sbkUnusedVariableCast(cppArgRemoved); @@ -3437,10 +3153,9 @@ void CppGenerator::writeSingleFunctionCall(TextStream &s, continue; auto argType = getArgumentType(func, argIdx); int argPos = argIdx - removedArgs; - QString argName = CPP_ARG + QString::number(argPos); QString pyArgName = usePyArgs ? pythonArgsAt(argPos) : PYTHON_ARG; indirections[argIdx] = - writeArgumentConversion(s, argType, argName, pyArgName, errorReturn, + writeArgumentConversion(s, argType, CPP_ARG_N(argPos), pyArgName, errorReturn, func->implementingClass(), arg.defaultValueExpression(), func->isUserAdded()); } @@ -3449,7 +3164,7 @@ void CppGenerator::writeSingleFunctionCall(TextStream &s, int numRemovedArgs = OverloadData::numberOfRemovedArguments(func); - s << "if (!PyErr_Occurred()) {\n" << indent; + s << "if (Shiboken::Errors::occurred() == nullptr) {\n" << indent; writeMethodCall(s, func, context, overloadData.pythonFunctionWrapperUsesListOfArguments(), func->arguments().size() - numRemovedArgs, indirections, errorReturn); @@ -3500,9 +3215,10 @@ void CppGenerator::writeCppToPythonFunction(TextStream &s, const QString &code, { QString prettyCode = code; - processCodeSnip(prettyCode); + const QString funcName = cppToPythonFunctionName(sourceTypeName, targetTypeName); + processCodeSnip(prettyCode, funcName); - s << "static PyObject *" << cppToPythonFunctionName(sourceTypeName, targetTypeName) + s << "static PyObject *" << funcName << "(const void *cppIn)\n{\n" << indent << prettyCode << ensureEndl << outdent << "}\n"; } @@ -3540,10 +3256,22 @@ void CppGenerator::writeCppToPythonFunction(TextStream &s, replaceCppToPythonVariables(code, getFullTypeName(ownerType), constRef); writeCppToPythonFunction(s, code, fixedCppTypeName(customConversion->ownerType())); } + +QString CppGenerator::containerNativeToTargetTypeName(const ContainerTypeEntryCPtr &type) +{ + QString result = type->targetLangApiName(); + if (result != cPyObjectT) { + result = containerCpythonBaseName(type); + if (result == cPySequenceT) + result = cPyListT; + } + return result; +} + void CppGenerator::writeCppToPythonFunction(TextStream &s, const AbstractMetaType &containerType) const { Q_ASSERT(containerType.typeEntry()->isContainer()); - auto cte = qSharedPointerCast<const ContainerTypeEntry>(containerType.typeEntry()); + auto cte = std::static_pointer_cast<const ContainerTypeEntry>(containerType.typeEntry()); if (!cte->hasCustomConversion()) { QString m; QTextStream(&m) << "Can't write the C++ to Python conversion function for container type '" @@ -3561,16 +3289,18 @@ void CppGenerator::writeCppToPythonFunction(TextStream &s, const AbstractMetaTyp code.replace(u"%INTYPE_"_s + QString::number(i), typeName); } replaceCppToPythonVariables(code, getFullTypeNameWithoutModifiers(containerType), true); - processCodeSnip(code); - writeCppToPythonFunction(s, code, fixedCppTypeName(containerType)); + processCodeSnip(code, containerType.typeEntry()->qualifiedCppName()); + writeCppToPythonFunction(s, code, fixedCppTypeName(containerType), + containerNativeToTargetTypeName(cte)); } void CppGenerator::writePythonToCppFunction(TextStream &s, const QString &code, const QString &sourceTypeName, const QString &targetTypeName) const { QString prettyCode = code; - processCodeSnip(prettyCode); - s << "static void " << pythonToCppFunctionName(sourceTypeName, targetTypeName) + const QString funcName = pythonToCppFunctionName(sourceTypeName, targetTypeName); + processCodeSnip(prettyCode, funcName); + s << "static void " << funcName << "(PyObject *pyIn, void *cppOut)\n{\n" << indent << prettyCode << ensureEndl << outdent << "}\n"; } @@ -3592,7 +3322,7 @@ void CppGenerator::writeIsPythonConvertibleToCppFunction(TextStream &s, << "return Shiboken::Conversions::nonePythonToCppNullPtr;\n" << outdent; } else { if (!condition.contains(u"pyIn")) - s << sbkUnusedVariableCast(u"pyIn"_s); + s << sbkUnusedVariableCast("pyIn"); } s << "if (" << condition << ")\n" << indent << "return " << pythonToCppFuncName << ";\n" << outdent @@ -3659,8 +3389,6 @@ void CppGenerator::writePythonToCppConversionFunctions(TextStream &s, QString pyTypeName = toNative.sourceTypeName(); if (pyTypeName == u"Py_None" || pyTypeName == u"PyNone") typeCheck = u"%in == Py_None"_s; - else if (pyTypeName == u"SbkEnumType") - typeCheck = u"Shiboken::isShibokenEnum(%in)"_s; else if (pyTypeName == u"SbkObject") typeCheck = u"Shiboken::Object::checkType(%in)"_s; } @@ -3675,29 +3403,26 @@ void CppGenerator::writePythonToCppConversionFunctions(TextStream &s, + cpythonTypeNameExt(toNative.sourceType()) + u')'; } typeCheck.replace(u"%in"_s, u"pyIn"_s); - processCodeSnip(typeCheck); + processCodeSnip(typeCheck, targetType->qualifiedCppName()); writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, typeCheck); } void CppGenerator::writePythonToCppConversionFunctions(TextStream &s, const AbstractMetaType &containerType) const { Q_ASSERT(containerType.typeEntry()->isContainer()); - auto cte = qSharedPointerCast<const ContainerTypeEntry>(containerType.typeEntry()); - if (!cte->hasCustomConversion()) { - //qFatal - return; - } - + const auto cte = std::static_pointer_cast<const ContainerTypeEntry>(containerType.typeEntry()); const auto customConversion = cte->customConversion(); - const TargetToNativeConversions &toCppConversions = - customConversion->targetToNativeConversions(); - if (toCppConversions.isEmpty()) { - //qFatal - return; - } + for (const auto &conv : customConversion->targetToNativeConversions()) + writePythonToCppConversionFunction(s, containerType, conv); +} + +void CppGenerator::writePythonToCppConversionFunction(TextStream &s, + const AbstractMetaType &containerType, + const TargetToNativeConversion &conv) const +{ // Python to C++ conversion function. QString cppTypeName = getFullTypeNameWithoutModifiers(containerType); - QString code = toCppConversions.constFirst().conversion(); + QString code = conv.conversion(); const QString line = u"auto &cppOutRef = *reinterpret_cast<"_s + cppTypeName + u" *>(cppOut);"_s; CodeSnipAbstract::prependCode(&code, line); @@ -3725,7 +3450,8 @@ void CppGenerator::writePythonToCppConversionFunctions(TextStream &s, const Abst code.replace(u"%in"_s, u"pyIn"_s); code.replace(u"%out"_s, u"cppOutRef"_s); QString typeName = fixedCppTypeName(containerType); - writePythonToCppFunction(s, code, typeName, typeName); + const QString &sourceTypeName = conv.sourceTypeName(); + writePythonToCppFunction(s, code, sourceTypeName, typeName); // Python to C++ convertible check function. QString typeCheck = cpythonCheckFunction(containerType); @@ -3733,7 +3459,7 @@ void CppGenerator::writePythonToCppConversionFunctions(TextStream &s, const Abst typeCheck = u"false"_s; else typeCheck = typeCheck + u"pyIn)"_s; - writeIsPythonConvertibleToCppFunction(s, typeName, typeName, typeCheck); + writeIsPythonConvertibleToCppFunction(s, sourceTypeName, typeName, typeCheck); s << '\n'; } @@ -3774,17 +3500,20 @@ static bool forceQObjectNamedArguments(const AbstractMetaFunctionCPtr &func) { if (func->functionType() != AbstractMetaFunction::ConstructorFunction) return false; - auto *owner = func->ownerClass(); + const auto owner = func->ownerClass(); Q_ASSERT(owner); - if (!owner->isQObject()) + if (!isQObject(owner)) return false; const QString &name = owner->name(); return name == u"QVBoxLayout" || name == u"QHBoxLayout" || name == u"QSplitterHandle" || name == u"QSizeGrip"; } -void CppGenerator::writeNamedArgumentResolution(TextStream &s, const AbstractMetaFunctionCPtr &func, - bool usePyArgs, const OverloadData &overloadData) const +void CppGenerator::writeNamedArgumentResolution(TextStream &s, + const AbstractMetaFunctionCPtr &func, + bool usePyArgs, + const OverloadData &overloadData, + ErrorReturn errorReturn) { const AbstractMetaArgumentList &args = OverloadData::getArgumentsWithDefaultValues(func); const bool hasDefaultArguments = !args.isEmpty(); @@ -3793,10 +3522,10 @@ void CppGenerator::writeNamedArgumentResolution(TextStream &s, const AbstractMet if (!hasDefaultArguments && !force) { if (overloadData.hasArgumentWithDefaultValue()) { // PySide-535: Allow for empty dict instead of nullptr in PyPy - s << "if (kwds && PyDict_Size(kwds) > 0) {\n" << indent + s << "if (kwds != nullptr && PyDict_Size(kwds) > 0) {\n" << indent << "errInfo.reset(kwds);\n" << "Py_INCREF(errInfo.object());\n" - << "goto " << cpythonFunctionName(func) << "_TypeError;\n" + << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n" << outdent << "}\n"; } return; @@ -3815,19 +3544,19 @@ void CppGenerator::writeNamedArgumentResolution(TextStream &s, const AbstractMet QString pyKeyName = u"key_"_s + arg.name(); s << "static PyObject *const " << pyKeyName << " = Shiboken::String::createStaticString(\"" << arg.name() << "\");\n" - << "if (PyDict_Contains(kwds, " << pyKeyName << ")) {\n" << indent + << "if (PyDict_Contains(kwds, " << pyKeyName << ") != 0) {\n" << indent << "value = PyDict_GetItem(kwds, " << pyKeyName << ");\n" - << "if (value && " << pyArgName << ") {\n" << indent - << "errInfo.reset(" << pyKeyName << ");\n" + << "if (value != nullptr && " << pyArgName << " != nullptr ) {\n" + << indent << "errInfo.reset(" << pyKeyName << ");\n" << "Py_INCREF(errInfo.object());\n" - << "goto " << cpythonFunctionName(func) << "_TypeError;\n" - << outdent << "}\nif (value) {\n" << indent + << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n" + << outdent << "}\nif (value != nullptr) {\n" << indent << pyArgName << " = value;\nif (!"; const auto &type = arg.modifiedType(); writeTypeCheck(s, type, pyArgName, isNumber(type.typeEntry()), {}); s << ")\n" << indent - << "goto " << cpythonFunctionName(func) << "_TypeError;\n" << outdent - << outdent + << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n" + << outdent << outdent << "}\nPyDict_DelItem(kwds_dup, " << pyKeyName << ");\n" << outdent << "}\n"; } @@ -3837,8 +3566,8 @@ void CppGenerator::writeNamedArgumentResolution(TextStream &s, const AbstractMet // until extra keyword signals and properties are handled. s << "if (PyDict_Size(kwds_dup) > 0) {\n" << indent << "errInfo.reset(kwds_dup.release());\n"; - if (!(func->isConstructor() && func->ownerClass()->isQObject())) - s << "goto " << cpythonFunctionName(func) << "_TypeError;\n"; + if (!(func->isConstructor() && isQObject(func->ownerClass()))) + s << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n"; else s << "// fall through to handle extra keyword signals and properties\n"; s << outdent << "}\n" @@ -3850,7 +3579,7 @@ QString CppGenerator::argumentNameFromIndex(const ApiExtractorResult &api, { switch (argIndex) { case -1: - return u"self"_s; + return PYTHON_SELF_VAR; case 0: return PYTHON_RETURN_VAR; case 1: { // Single argument? @@ -3863,7 +3592,7 @@ QString CppGenerator::argumentNameFromIndex(const ApiExtractorResult &api, return pythonArgsAt(argIndex - 1); } -const AbstractMetaClass * +AbstractMetaClassCPtr CppGenerator::argumentClassFromIndex(const ApiExtractorResult &api, const AbstractMetaFunctionCPtr &func, int argIndex) { @@ -3888,7 +3617,7 @@ CppGenerator::argumentClassFromIndex(const ApiExtractorResult &api, auto te = type.typeEntry(); if (type.isVoid() || !te->isComplex()) throw Exception(msgInvalidArgumentModification(func, argIndex)); - auto *result = AbstractMetaClass::findClass(api.classes(), te); + const auto result = AbstractMetaClass::findClass(api.classes(), te); if (!result) throw Exception(msgClassNotFound(te)); return result; @@ -3914,6 +3643,11 @@ if (errorType != nullptr) PyErr_SetObject(errorType, errorString); )"; +static QString explicitConversion(const QString &v, const AbstractMetaType &t) +{ + return t.plainType().cppSignature() + u'(' + v + u')'; +} + void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr &func, const GeneratorContext &context, bool usesPyArgs, int maxArgs, @@ -3987,7 +3721,7 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr if (hasConversionRule) userArgs << arg.name() + CONV_RULE_OUT_VAR_SUFFIX; else if (!arg.defaultValueExpression().isEmpty()) - userArgs.append(CPP_ARG_REMOVED + QString::number(i)); + userArgs.append(CPP_ARG_REMOVED(i)); } else { if (hasConversionRule) { userArgs.append(arg.name() + CONV_RULE_OUT_VAR_SUFFIX); @@ -3995,14 +3729,16 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr const int idx = arg.argumentIndex() - removedArgs; const auto deRef = argumentIndirections.at(i); QString argName = AbstractMetaType::dereferencePrefix(deRef) - + CPP_ARG + QString::number(idx); + + CPP_ARG_N(idx); userArgs.append(argName); } } // "Pass unique ptr by value" pattern: Apply std::move() auto type = arg.type(); - if (type.isUniquePointer() && type.passByValue()) + if (type.useStdMove()) userArgs.last() = stdMove(userArgs.constLast()); + else if (type.viewOn() != nullptr) + userArgs.last() = explicitConversion(userArgs.constLast(), type); } // If any argument's default value was modified the method must be called @@ -4024,7 +3760,7 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr if (hasConversionRule) otherArgs.prepend(arg.name() + CONV_RULE_OUT_VAR_SUFFIX); else - otherArgs.prepend(CPP_ARG_REMOVED + QString::number(i)); + otherArgs.prepend(CPP_ARG_REMOVED(i)); } if (otherArgsModified) userArgs << otherArgs; @@ -4072,21 +3808,21 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr Q_ASSERT(owner == context.metaClass()); if (func->functionType() == AbstractMetaFunction::CopyConstructorFunction && maxArgs == 1) { - mc << "new ::" << context.effectiveClassName() + mc << "new " << globalScopePrefix(context) << context.effectiveClassName() << "(*" << CPP_ARG0 << ')'; } else { const QString ctorCall = context.effectiveClassName() + u'(' + userArgs.join(u", "_s) + u')'; - if (usePySideExtensions() && owner->isQObject()) { + if (usePySideExtensions() && isQObject(owner)) { s << "void *addr = PySide::nextQObjectMemoryAddr();\n"; - uva << "if (addr) {\n" << indent - << "cptr = new (addr) ::" << ctorCall << ";\n" - << "PySide::setNextQObjectMemoryAddr(nullptr);\n" << outdent + uva << "if (addr != nullptr) {\n" << indent + << "cptr = new (addr) " << globalScopePrefix(context) << ctorCall + << ";\nPySide::setNextQObjectMemoryAddr(nullptr);\n" << outdent << "} else {\n" << indent - << "cptr = new ::" << ctorCall << ";\n" + << "cptr = new " << globalScopePrefix(context) << ctorCall << ";\n" << outdent << "}\n"; } else { - mc << "new ::" << ctorCall; + mc << "new " << globalScopePrefix(context) << ctorCall; } } } else { @@ -4100,7 +3836,7 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr const bool hasWrapper = shouldGenerateCppWrapper(ownerClass); if (!avoidProtectedHack() || !func->isProtected() || !hasWrapper) { if (func->isStatic()) { - mc << "::" << methodCallClassName << "::"; + mc << m_gsp << methodCallClassName << "::"; } else { const QString cppSelfVar = CPP_SELF_VAR; const QString selfVarCast = func->ownerClass() == func->implementingClass() @@ -4109,7 +3845,7 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr + u" *>("_s + cppSelfVar + u')'; if (func->isConstant()) { if (avoidProtectedHack()) { - mc << "const_cast<const ::"; + mc << "const_cast<const " << globalScopePrefix(context); if (ownerClass->cppWrapper().testFlag(AbstractMetaClass::CppProtectedHackWrapper)) { // PYSIDE-500: Need a special wrapper cast when inherited const QString selfWrapCast = ownerClass == func->implementingClass() @@ -4124,7 +3860,7 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr mc << " *>(" << selfVarCast << ")->"; } } else { - mc << "const_cast<const ::" << methodCallClassName; + mc << "const_cast<const " << m_gsp << methodCallClassName; mc << " *>(" << selfVarCast << ")->"; } } else { @@ -4140,13 +3876,13 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr if (!func->isStatic()) { const bool directInheritance = context.metaClass() == ownerClass; mc << (directInheritance ? "static_cast" : "reinterpret_cast") - << "<::" << wrapperName(ownerClass) << " *>(" << CPP_SELF_VAR << ")->"; + << '<' << wrapperName(ownerClass) << " *>(" + << CPP_SELF_VAR << ")->"; } if (!func->isAbstract()) mc << (func->isProtected() ? wrapperName(func->ownerClass()) : - u"::"_s - + methodCallClassName) << "::"; + m_gsp + methodCallClassName) << "::"; mc << func->originalName() << "_protected"; } } else { @@ -4297,8 +4033,8 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr s << "Shiboken::Object::"; if (ownership == TypeSystem::TargetLangOwnership) { s << "getOwnership(" << pyArgName << ");"; - } else if (auto *ac = argumentClassFromIndex(api(), func, argIndex); - ac->hasVirtualDestructor()) { + } else if (auto ac = argumentClassFromIndex(api(), func, argIndex); + ac && ac->hasVirtualDestructor()) { s << "releaseOwnership(" << pyArgName << ");"; } else { s << "invalidate(" << pyArgName << ");"; @@ -4343,12 +4079,12 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr s << propagateException; } -QStringList CppGenerator::getAncestorMultipleInheritance(const AbstractMetaClass *metaClass) +QStringList CppGenerator::getAncestorMultipleInheritance(const AbstractMetaClassCPtr &metaClass) { QStringList result; const auto &baseClases = metaClass->typeSystemBaseClasses(); if (!baseClases.isEmpty()) { - for (const AbstractMetaClass *baseClass : baseClases) { + for (const auto &baseClass : baseClases) { QString offset; QTextStream(&offset) << "reinterpret_cast<uintptr_t>(static_cast<const " << baseClass->qualifiedCppName() << " *>(class_ptr)) - base"; @@ -4361,50 +4097,55 @@ QStringList CppGenerator::getAncestorMultipleInheritance(const AbstractMetaClass result.append(offset); } - for (const AbstractMetaClass *baseClass : baseClases) + for (const auto &baseClass : baseClases) result.append(getAncestorMultipleInheritance(baseClass)); } return result; } -void CppGenerator::writeMultipleInheritanceInitializerFunction(TextStream &s, const AbstractMetaClass *metaClass) +void CppGenerator::writeMultipleInheritanceInitializerFunction(TextStream &s, + const AbstractMetaClassCPtr &metaClass) { QString className = metaClass->qualifiedCppName(); const QStringList ancestors = getAncestorMultipleInheritance(metaClass); - s << "static int mi_offsets[] = { "; - for (qsizetype i = 0; i < ancestors.size(); i++) - s << "-1, "; - s << "-1 };\n" - << "int *\n" + s << "int *\n" << multipleInheritanceInitializerFunctionName(metaClass) << "(const void *cptr)\n" - << "{\n" << indent - << "if (mi_offsets[0] == -1) {\n" << indent - << "std::set<int> offsets;\n" - << "const auto *class_ptr = reinterpret_cast<const " << className << " *>(cptr);\n" - << "const auto base = reinterpret_cast<uintptr_t>(class_ptr);\n"; + << "{\n" << indent; + s << "static int mi_offsets[] = {-2"; + for (qsizetype i = 0; i < ancestors.size(); i++) + s << ", 0"; + s << "};\n" + << "if (mi_offsets[0] == -2) {\n" << indent + << "const auto *class_ptr = reinterpret_cast<const " << className << " *>(cptr);\n" + << "const auto base = reinterpret_cast<uintptr_t>(class_ptr);\n" + << "int *p = mi_offsets;\n"; for (const QString &ancestor : ancestors) - s << "offsets.insert(int(" << ancestor << "));\n"; - - s << "\noffsets.erase(0);\n\n" - << "std::copy(offsets.cbegin(), offsets.cend(), mi_offsets);\n" << outdent + s << "*p++ = int(" << ancestor << ");\n"; + s << "std::sort(mi_offsets, p);\n" + << "auto *end = std::unique(mi_offsets, p);\n" + << "*end++ = -1;\n" + << "if (mi_offsets[0] == 0)\n" + << indent + << "std::memmove(&mi_offsets[0], &mi_offsets[1], (end - mi_offsets - 1) * sizeof(int));\n" + << outdent << outdent << "}\nreturn mi_offsets;\n" << outdent << "}\n"; } -void CppGenerator::writeSpecialCastFunction(TextStream &s, const AbstractMetaClass *metaClass) +void CppGenerator::writeSpecialCastFunction(TextStream &s, const AbstractMetaClassCPtr &metaClass) { QString className = metaClass->qualifiedCppName(); s << "static void * " << cpythonSpecialCastFunctionName(metaClass) << "(void *obj, PyTypeObject *desiredType)\n{\n" << indent - << "auto me = reinterpret_cast< ::" << className << " *>(obj);\n"; + << "auto me = reinterpret_cast< " << m_gsp << className << " *>(obj);\n"; bool firstClass = true; const auto &allAncestors = metaClass->allTypeSystemAncestors(); - for (const AbstractMetaClass *baseClass : allAncestors) { + for (const auto &baseClass : allAncestors) { if (!firstClass) s << "else "; s << "if (desiredType == " << cpythonTypeNameExt(baseClass->typeEntry()) << ")\n" << indent - << "return static_cast< ::" << baseClass->qualifiedCppName() << " *>(me);\n" + << "return static_cast< " << getFullTypeName(baseClass) << " *>(me);\n" << outdent; firstClass = false; } @@ -4420,79 +4161,28 @@ void CppGenerator::writePrimitiveConverterInitialization(TextStream &s, << converter << " = Shiboken::Conversions::createConverter("; if (!type->hasTargetLangApiType()) s << "nullptr"; - else if (type->targetLangApiName() == cPyObjectT()) + else if (type->targetLangApiName() == cPyObjectT) s << "&PyBaseObject_Type"; else s << '&' << type->targetLangApiName() << "_Type"; QString typeName = fixedCppTypeName(type); s << ", " << cppToPythonFunctionName(typeName, typeName) << ");\n" - << "Shiboken::Conversions::registerConverterName(" << converter << ", \"" - << type->qualifiedCppName() << "\");\n"; + << registerConverterName(type->qualifiedCppName(), converter); writeCustomConverterRegister(s, customConversion, converter); } -static void registerEnumConverterScopes(TextStream &s, QString signature) +static void registerConverterInScopes(TextStream &s, QStringView signature, + QAnyStringView varName = converterVar) { while (true) { - s << "Shiboken::Conversions::registerConverterName(converter, \"" - << signature << "\");\n"; - const int qualifierPos = signature.indexOf(u"::"); - if (qualifierPos != -1) - signature.remove(0, qualifierPos + 2); - else + s << registerConverterName(signature, varName); + const auto qualifierPos = signature.indexOf("::"_L1); + if (qualifierPos == -1) break; + signature = signature.sliced(qualifierPos + 2); } } -void CppGenerator::writeFlagsConverterInitialization(TextStream &s, - const FlagsTypeEntryCPtr &flags) -{ - static const char enumPythonVar[] = "FType"; - - const QString qualifiedCppName = flags->qualifiedCppName(); - s << "// Register converter for flag '" << qualifiedCppName << "'.\n{\n" - << indent; - QString typeName = fixedCppTypeName(flags); - s << "SbkConverter *converter = Shiboken::Conversions::createConverter(" - << enumPythonVar << ',' << '\n' << indent - << cppToPythonFunctionName(typeName, typeName) << ");\n" << outdent; - - const QString enumTypeName = fixedCppTypeName(flags->originator()); - QString toCpp = pythonToCppFunctionName(enumTypeName, typeName); - QString isConv = convertibleToCppFunctionName(enumTypeName, typeName); - writeAddPythonToCppConversion(s, u"converter"_s, toCpp, isConv); - toCpp = pythonToCppFunctionName(typeName, typeName); - isConv = convertibleToCppFunctionName(typeName, typeName); - writeAddPythonToCppConversion(s, u"converter"_s, toCpp, isConv); - toCpp = pythonToCppFunctionName(u"number"_s, typeName); - isConv = convertibleToCppFunctionName(u"number"_s, typeName); - writeAddPythonToCppConversion(s, u"converter"_s, toCpp, isConv); - s << "Shiboken::Enum::setTypeConverter(" << enumPythonVar - << ", converter, true);\n"; - // Replace "QFlags<Class::Option>" by "Class::Options" - QString signature = qualifiedCppName; - if (qualifiedCppName.startsWith(u"QFlags<") && qualifiedCppName.endsWith(u'>')) { - signature.chop(1); - signature.remove(0, 7); - const int lastQualifierPos = signature.lastIndexOf(u"::"); - if (lastQualifierPos != -1) { - signature.replace(lastQualifierPos + 2, signature.size() - lastQualifierPos - 2, - flags->flagsName()); - } else { - signature = flags->flagsName(); - } - } - - registerEnumConverterScopes(s, signature); - - // PYSIDE-1673: Also register "QFlags<Class::Option>" purely for - // the purpose of finding the converter by QVariant::typeName() - // in the QVariant conversion code. - s << "Shiboken::Conversions::registerConverterName(converter, \"" - << flags->name() << "\");\n" - << outdent << "}\n"; -} - void CppGenerator::writeEnumConverterInitialization(TextStream &s, const AbstractMetaEnum &metaEnum) { if (metaEnum.isPrivate() || metaEnum.isAnonymous()) @@ -4514,81 +4204,71 @@ void CppGenerator::writeEnumConverterInitialization(TextStream &s, const Abstrac const QString isConv = convertibleToCppFunctionName(typeName, typeName); writeAddPythonToCppConversion(s, u"converter"_s, toCpp, isConv); s << "Shiboken::Enum::setTypeConverter(" << enumPythonVar - << ", converter, false);\n"; + << ", converter);\n"; - registerEnumConverterScopes(s, enumType->qualifiedCppName()); + registerConverterInScopes(s, enumType->qualifiedCppName()); + if (auto flags = enumType->flags()) + s << "// Register converter for flag '" << flags->qualifiedCppName() << "'.\n" + << registerConverterName(flags->name()) // QMetaType + << registerConverterName(flags->originalName()); // Signals with flags s << outdent << "}\n"; - - if (auto flags = enumType->flags(); !flags.isNull()) - writeFlagsConverterInitialization(s, flags); } -QString CppGenerator::writeContainerConverterInitialization(TextStream &s, const AbstractMetaType &type) const +QString CppGenerator::writeContainerConverterInitialization(TextStream &s, + const AbstractMetaType &type, + const ApiExtractorResult &api) { - QByteArray cppSignature = QMetaObject::normalizedSignature(type.cppSignature().toUtf8()); + const auto cppSignature = + QString::fromUtf8(QMetaObject::normalizedSignature(type.cppSignature().toUtf8())); s << "// Register converter for type '" << cppSignature << "'.\n"; - QString converter = converterObject(type); + const QString converter = converterObject(type); s << converter << " = Shiboken::Conversions::createConverter("; - if (type.typeEntry()->targetLangApiName() == cPyObjectT()) { + + Q_ASSERT(type.typeEntry()->isContainer()); + const auto typeEntry = std::static_pointer_cast<const ContainerTypeEntry>(type.typeEntry()); + + const QString targetTypeName = containerNativeToTargetTypeName(typeEntry); + if (targetTypeName == cPyObjectT) { s << "&PyBaseObject_Type"; } else { - QString baseName = cpythonBaseName(type.typeEntry()); - if (baseName == cPySequenceT()) - baseName = cPyListT(); - s << '&' << baseName << "_Type"; - } - QString typeName = fixedCppTypeName(type); - s << ", " << cppToPythonFunctionName(typeName, typeName) << ");\n"; - QString toCpp = pythonToCppFunctionName(typeName, typeName); - QString isConv = convertibleToCppFunctionName(typeName, typeName); - s << "Shiboken::Conversions::registerConverterName(" << converter << ", \"" << cppSignature << "\");\n"; - if (usePySideExtensions() && cppSignature.startsWith("const ") && cppSignature.endsWith("&")) { - cppSignature.chop(1); - cppSignature.remove(0, sizeof("const ") / sizeof(char) - 1); - s << "Shiboken::Conversions::registerConverterName(" << converter << ", \"" << cppSignature << "\");\n"; + s << '&' << targetTypeName << "_Type"; } - const QString converterObj = converterObject(type); - writeAddPythonToCppConversion(s, converterObj, toCpp, isConv); - return converterObj; -} - -void CppGenerator::writeSmartPointerConverterInitialization(TextStream &s, const AbstractMetaType &type) const -{ - const QByteArray cppSignature = type.cppSignature().toUtf8(); - auto writeConversionRegister = [&s](const AbstractMetaType &sourceType, const QString &targetTypeName, const QString &targetConverter) - { - const QString sourceTypeName = fixedCppTypeName(sourceType); - const QString toCpp = pythonToCppFunctionName(sourceTypeName, targetTypeName); - const QString isConv = convertibleToCppFunctionName(sourceTypeName, targetTypeName); - writeAddPythonToCppConversion(s, targetConverter, toCpp, isConv); - }; - - const auto classes = findSmartPointeeBaseClasses(api(), type); - if (classes.isEmpty()) - return; + const QString typeName = fixedCppTypeName(type); + s << ", " << cppToPythonFunctionName(typeName, targetTypeName) << ");\n"; - auto smartPointerTypeEntry = qSharedPointerCast<const SmartPointerTypeEntry>(type.typeEntry()); + s << registerConverterName(cppSignature, converter); + if (usePySideExtensions() && cppSignature.startsWith("const "_L1) + && cppSignature.endsWith(u'&')) { + auto underlyingType = QStringView{cppSignature}.sliced(6, cppSignature.size() - 7); + s << registerConverterName(underlyingType, converter); + } - s << "// Register SmartPointer converter for type '" << cppSignature << "'." << '\n' - << "///////////////////////////////////////////////////////////////////////////////////////\n\n"; + for (const auto &conv : typeEntry->customConversion()->targetToNativeConversions()) { + const QString &sourceTypeName = conv.sourceTypeName(); + QString toCpp = pythonToCppFunctionName(sourceTypeName, typeName); + QString isConv = convertibleToCppFunctionName(sourceTypeName, typeName); + writeAddPythonToCppConversion(s, converter, toCpp, isConv); + } - for (auto *base : classes) { - auto baseTe = base->typeEntry(); - if (auto opt = findSmartPointerInstantiation(smartPointerTypeEntry, baseTe)) { - const auto smartTargetType = opt.value(); - s << "// Convert to SmartPointer derived class: [" - << smartTargetType.cppSignature() << "]\n"; - const QString converter = u"Shiboken::Conversions::getConverter(\""_s - + smartTargetType.cppSignature() + u"\")"_s; - writeConversionRegister(type, fixedCppTypeName(smartTargetType), converter); - } else { - s << "// Class not found:" << type.instantiations().at(0).cppSignature(); + auto typedefItPair = api.typedefTargetToName().equal_range(type.cppSignature()); + if (typedefItPair.first != typedefItPair.second) { + auto *typeDb = TypeDatabase::instance(); + s << "// Register converters for type aliases of " << cppSignature << "'.\n"; + for (auto it = typedefItPair.first; it != typedefItPair.second; ++it) { + if (!typeDb->findType(it.value())) + s << registerConverterName(it.value(), converter); } } - s << "///////////////////////////////////////////////////////////////////////////////////////" << '\n' << '\n'; + return converter; +} + +QString CppGenerator::typeInitStruct(const TypeEntryCPtr &te) +{ + return cppApiVariableName(te->targetLangPackage()) + u'[' + + getTypeIndexVariableName(te) + u']'; } void CppGenerator::writeExtendedConverterInitialization(TextStream &s, @@ -4597,23 +4277,23 @@ void CppGenerator::writeExtendedConverterInitialization(TextStream &s, { s << "// Extended implicit conversions for " << externalType->qualifiedTargetLangName() << ".\n"; - for (const AbstractMetaClass *sourceClass : conversions) { - const QString converterVar = cppApiVariableName(externalType->targetLangPackage()) + u'[' - + getTypeIndexVariableName(externalType) + u']'; + for (const auto &sourceClass : conversions) { QString sourceTypeName = fixedCppTypeName(sourceClass->typeEntry()); QString targetTypeName = fixedCppTypeName(externalType); QString toCpp = pythonToCppFunctionName(sourceTypeName, targetTypeName); QString isConv = convertibleToCppFunctionName(sourceTypeName, targetTypeName); - writeAddPythonToCppConversion(s, converterVar, toCpp, isConv); + if (!externalType->isPrimitive()) + s << cpythonTypeNameExt(externalType) << ";\n"; + writeAddPythonToCppConversion(s, typeInitStruct(externalType), toCpp, isConv); } } -QString CppGenerator::multipleInheritanceInitializerFunctionName(const AbstractMetaClass *metaClass) +QString CppGenerator::multipleInheritanceInitializerFunctionName(const AbstractMetaClassCPtr &metaClass) { return cpythonBaseName(metaClass->typeEntry()) + u"_mi_init"_s; } -bool CppGenerator::supportsMappingProtocol(const AbstractMetaClass *metaClass) +bool CppGenerator::supportsMappingProtocol(const AbstractMetaClassCPtr &metaClass) { for (const auto &m : mappingProtocols()) { if (metaClass->hasFunction(m.name)) @@ -4623,7 +4303,7 @@ bool CppGenerator::supportsMappingProtocol(const AbstractMetaClass *metaClass) return false; } -bool CppGenerator::supportsNumberProtocol(const AbstractMetaClass *metaClass) const +bool CppGenerator::supportsNumberProtocol(const AbstractMetaClassCPtr &metaClass) { return metaClass->hasArithmeticOperatorOverload() || metaClass->hasIncDecrementOperatorOverload() @@ -4632,7 +4312,7 @@ bool CppGenerator::supportsNumberProtocol(const AbstractMetaClass *metaClass) co || hasBoolCast(metaClass); } -bool CppGenerator::supportsSequenceProtocol(const AbstractMetaClass *metaClass) +bool CppGenerator::supportsSequenceProtocol(const AbstractMetaClassCPtr &metaClass) { for (const auto &seq : sequenceProtocols()) { if (metaClass->hasFunction(seq.name)) @@ -4643,7 +4323,7 @@ bool CppGenerator::supportsSequenceProtocol(const AbstractMetaClass *metaClass) return baseType && baseType->isContainer(); } -bool CppGenerator::shouldGenerateGetSetList(const AbstractMetaClass *metaClass) const +bool CppGenerator::shouldGenerateGetSetList(const AbstractMetaClassCPtr &metaClass) { for (const AbstractMetaField &f : metaClass->fields()) { if (!f.isStatic()) @@ -4661,29 +4341,24 @@ bool CppGenerator::shouldGenerateGetSetList(const AbstractMetaClass *metaClass) struct pyTypeSlotEntry { - explicit pyTypeSlotEntry(const char *name, const QString &function) : + explicit pyTypeSlotEntry(QAnyStringView name, QAnyStringView function) : m_name(name), m_function(function) {} - const char *m_name; - const QString &m_function; + QAnyStringView m_name; + QAnyStringView m_function; }; TextStream &operator<<(TextStream &str, const pyTypeSlotEntry &e) { - str << '{' << e.m_name << ','; - const int padding = qMax(0, 18 - int(strlen(e.m_name))); - for (int p = 0; p < padding; ++p) - str << ' '; - if (e.m_function.isEmpty()) - str << NULL_PTR; - else - str << "reinterpret_cast<void *>(" << e.m_function << ')'; - str << "},\n"; + if (!e.m_function.isEmpty()) { + str << '{' << e.m_name << ',' << Pad(' ', qMax(0, 18 - e.m_name.size())) + << "reinterpret_cast<void *>(" << e.m_function << ")},\n"; + } return str; } void CppGenerator::writeClassDefinition(TextStream &s, - const AbstractMetaClass *metaClass, + const AbstractMetaClassCPtr &metaClass, const GeneratorContext &classContext) { QString tp_init; @@ -4692,7 +4367,6 @@ void CppGenerator::writeClassDefinition(TextStream &s, QString tp_hash; QString tp_call; const QString className = chopType(cpythonTypeName(metaClass)); - QString baseClassName; AbstractMetaFunctionCList ctors; const auto &allCtors = metaClass->queryFunctions(FunctionQueryOption::AnyConstructor); for (const auto &f : allCtors) { @@ -4702,13 +4376,10 @@ void CppGenerator::writeClassDefinition(TextStream &s, } } - if (!metaClass->baseClass()) - baseClassName = u"SbkObject_TypeF()"_s; - bool onlyPrivCtor = !metaClass->hasNonPrivateConstructor(); const bool isQApp = usePySideExtensions() - && metaClass->inheritsFrom(u"QCoreApplication"_s); + && inheritsFrom(metaClass, u"QCoreApplication"_s); QString tp_flags = u"Py_TPFLAGS_DEFAULT"_s; if (!metaClass->attributes().testFlag(AbstractMetaClass::FinalCppClass)) @@ -4760,25 +4431,31 @@ void CppGenerator::writeClassDefinition(TextStream &s, if (generateRichComparison(classContext)) tp_richcompare = cpythonBaseName(metaClass) + u"_richcompare"_s; + const bool isSmartPointer = classContext.forSmartPointer(); QString tp_getset; - if (shouldGenerateGetSetList(metaClass) && !classContext.forSmartPointer()) + if (shouldGenerateGetSetList(metaClass) && !isSmartPointer) tp_getset = cpythonGettersSettersDefinitionName(metaClass); // search for special functions clearTpFuncs(); for (const auto &func : metaClass->functions()) { - if (m_tpFuncs.contains(func->name())) - m_tpFuncs[func->name()] = cpythonFunctionName(func); + // Special non-operator functions identified by name + auto it = m_tpFuncs.find(func->name()); + if (it != m_tpFuncs.end()) + it.value() = cpythonFunctionName(func); + else if ( it = m_nbFuncs.find(func->name()); it != m_nbFuncs.end() ) + it.value() = cpythonFunctionName(func); } - if (m_tpFuncs.value(reprFunction()).isEmpty() - && metaClass->hasToStringCapability()) { - m_tpFuncs[reprFunction()] = writeReprFunction(s, - classContext, - metaClass->toStringCapabilityIndirections()); + if (m_tpFuncs.value(REPR_FUNCTION).isEmpty() + && (isSmartPointer || metaClass->hasToStringCapability())) { + const QString name = isSmartPointer + ? writeSmartPointerReprFunction(s, classContext) + : writeReprFunction(s, classContext, metaClass->toStringCapabilityIndirections()); + m_tpFuncs[REPR_FUNCTION] = name; } // class or some ancestor has multiple inheritance - const AbstractMetaClass *miClass = getMultipleInheritingClass(metaClass); + const auto miClass = getMultipleInheritingClass(metaClass); if (miClass) { if (metaClass == miClass) writeMultipleInheritanceInitializerFunction(s, metaClass); @@ -4792,8 +4469,8 @@ void CppGenerator::writeClassDefinition(TextStream &s, if (hasHashFunction(metaClass)) tp_hash = u'&' + cpythonBaseName(metaClass) + u"_HashFunc"_s; - const auto callOp = metaClass->findFunction(u"operator()"); - if (!callOp.isNull() && !callOp->isModifiedRemoved()) + const auto callOp = metaClass->findFunction("operator()"); + if (callOp && !callOp->isModifiedRemoved()) tp_call = u'&' + cpythonFunctionName(callOp); const QString typePtr = u"_"_s + className @@ -4804,7 +4481,7 @@ void CppGenerator::writeClassDefinition(TextStream &s, << "}\n\nstatic PyType_Slot " << className << "_slots[] = {\n" << indent << "{Py_tp_base, nullptr}, // inserted by introduceWrapperType\n" << pyTypeSlotEntry("Py_tp_dealloc", tp_dealloc) - << pyTypeSlotEntry("Py_tp_repr", m_tpFuncs.value(reprFunction())) + << pyTypeSlotEntry("Py_tp_repr", m_tpFuncs.value(REPR_FUNCTION)) << pyTypeSlotEntry("Py_tp_hash", tp_hash) << pyTypeSlotEntry("Py_tp_call", tp_call) << pyTypeSlotEntry("Py_tp_str", m_tpFuncs.value(u"__str__"_s)) @@ -4828,7 +4505,6 @@ void CppGenerator::writeClassDefinition(TextStream &s, writeTypeAsMappingDefinition(s, metaClass); } if (supportsNumberProtocol(metaClass)) { - // This one must come last. See the function itself. s << "// type supports number protocol\n"; writeTypeAsNumberDefinition(s, metaClass); } @@ -4843,12 +4519,12 @@ void CppGenerator::writeClassDefinition(TextStream &s, } void CppGenerator::writeMappingMethods(TextStream &s, - const AbstractMetaClass *metaClass, + const AbstractMetaClassCPtr &metaClass, const GeneratorContext &context) const { for (const auto & m : mappingProtocols()) { const auto func = metaClass->findFunction(m.name); - if (func.isNull()) + if (!func) continue; QString funcName = cpythonFunctionName(func); CodeSnipList snips = func->injectedCodeSnips(TypeSystem::CodeSnipPositionAny, TypeSystem::TargetLangCode); @@ -4865,14 +4541,14 @@ void CppGenerator::writeMappingMethods(TextStream &s, } void CppGenerator::writeSequenceMethods(TextStream &s, - const AbstractMetaClass *metaClass, + const AbstractMetaClassCPtr &metaClass, const GeneratorContext &context) const { bool injectedCode = false; for (const auto &seq : sequenceProtocols()) { const auto func = metaClass->findFunction(seq.name); - if (func.isNull()) + if (!func) continue; injectedCode = true; QString funcName = cpythonFunctionName(func); @@ -4896,25 +4572,25 @@ void CppGenerator::writeSequenceMethods(TextStream &s, static const QHash<QString, QString> &sqFuncs() { static const QHash<QString, QString> result = { - {u"__concat__"_s, u"sq_concat"_s}, - {u"__contains__"_s, u"sq_contains"_s}, - {u"__getitem__"_s, u"sq_item"_s}, - {u"__getslice__"_s, u"sq_slice"_s}, - {u"__len__"_s, u"sq_length"_s}, - {u"__setitem__"_s, u"sq_ass_item"_s}, - {u"__setslice__"_s, u"sq_ass_slice"_s} + {u"__concat__"_s, u"Py_sq_concat"_s}, + {u"__contains__"_s, u"Py_sq_contains"_s}, + {u"__getitem__"_s, u"Py_sq_item"_s}, + {u"__getslice__"_s, u"Py_sq_slice"_s}, + {u"__len__"_s, u"Py_sq_length"_s}, + {u"__setitem__"_s, u"Py_sq_ass_item"_s}, + {u"__setslice__"_s, u"Py_sq_ass_slice"_s} }; return result; } void CppGenerator::writeTypeAsSequenceDefinition(TextStream &s, - const AbstractMetaClass *metaClass) + const AbstractMetaClassCPtr &metaClass) { bool hasFunctions = false; QMap<QString, QString> funcs; for (const auto &seq : sequenceProtocols()) { const auto func = metaClass->findFunction(seq.name); - if (!func.isNull()) { + if (func) { funcs.insert(seq.name, u'&' + cpythonFunctionName(func)); hasFunctions = true; } @@ -4932,38 +4608,34 @@ void CppGenerator::writeTypeAsSequenceDefinition(TextStream &s, for (auto it = sqFuncs().cbegin(), end = sqFuncs().cend(); it != end; ++it) { const QString &sqName = it.key(); auto fit = funcs.constFind(sqName); - if (fit != funcs.constEnd()) { - s << "{Py_" << it.value() << ", reinterpret_cast<void *>(" - << fit.value() << ")},\n"; - } + if (fit != funcs.constEnd()) + s << pyTypeSlotEntry(it.value(), fit.value()); } } void CppGenerator::writeTypeAsMappingDefinition(TextStream &s, - const AbstractMetaClass *metaClass) + const AbstractMetaClassCPtr &metaClass) { // Sequence protocol structure members names static const QHash<QString, QString> mpFuncs{ - {u"__mlen__"_s, u"mp_length"_s}, - {u"__mgetitem__"_s, u"mp_subscript"_s}, - {u"__msetitem__"_s, u"mp_ass_subscript"_s}, + {u"__mlen__"_s, u"Py_mp_length"_s}, + {u"__mgetitem__"_s, u"Py_mp_subscript"_s}, + {u"__msetitem__"_s, u"Py_mp_ass_subscript"_s}, }; QMap<QString, QString> funcs; for (const auto &m : mappingProtocols()) { const auto func = metaClass->findFunction(m.name); - if (!func.isNull()) { + if (func) { const QString entry = u"reinterpret_cast<void *>(&"_s + cpythonFunctionName(func) + u')'; funcs.insert(m.name, entry); - } else { - funcs.insert(m.name, NULL_PTR); } } for (auto it = mpFuncs.cbegin(), end = mpFuncs.cend(); it != end; ++it) { const auto fit = funcs.constFind(it.key()); if (fit != funcs.constEnd()) - s << "{Py_" << it.value() << ", " << fit.value() << "},\n"; + s << pyTypeSlotEntry(it.value(), fit.value()); } } @@ -4971,105 +4643,102 @@ void CppGenerator::writeTypeAsMappingDefinition(TextStream &s, static const QHash<QString, QString> &nbFuncs() { static const QHash<QString, QString> result = { - {u"__add__"_s, u"nb_add"_s}, - {u"__sub__"_s, u"nb_subtract"_s}, - {u"__mul__"_s, u"nb_multiply"_s}, - {u"__div__"_s, u"nb_divide"_s}, - {u"__mod__"_s, u"nb_remainder"_s}, - {u"__neg__"_s, u"nb_negative"_s}, - {u"__pos__"_s, u"nb_positive"_s}, - {u"__invert__"_s, u"nb_invert"_s}, - {u"__lshift__"_s, u"nb_lshift"_s}, - {u"__rshift__"_s, u"nb_rshift"_s}, - {u"__and__"_s, u"nb_and"_s}, - {u"__xor__"_s, u"nb_xor"_s}, - {u"__or__"_s, u"nb_or"_s}, - {u"__iadd__"_s, u"nb_inplace_add"_s}, - {u"__isub__"_s, u"nb_inplace_subtract"_s}, - {u"__imul__"_s, u"nb_inplace_multiply"_s}, - {u"__idiv__"_s, u"nb_inplace_divide"_s}, - {u"__imod__"_s, u"nb_inplace_remainder"_s}, - {u"__ilshift__"_s, u"nb_inplace_lshift"_s}, - {u"__irshift__"_s, u"nb_inplace_rshift"_s}, - {u"__iand__"_s, u"nb_inplace_and"_s}, - {u"__ixor__"_s, u"nb_inplace_xor"_s}, - {u"__ior__"_s, u"nb_inplace_or"_s}, - {boolT(), u"nb_nonzero"_s} + {u"__abs__"_s, u"Py_nb_absolute"_s}, + {u"__add__"_s, u"Py_nb_add"_s}, + {u"__sub__"_s, u"Py_nb_subtract"_s}, + {u"__mul__"_s, u"Py_nb_multiply"_s}, + {u"__div__"_s, u"Py_nb_true_divide"_s}, + {u"__mod__"_s, u"Py_nb_remainder"_s}, + {u"__neg__"_s, u"Py_nb_negative"_s}, + {u"__pos__"_s, u"Py_nb_positive"_s}, + {u"__pow__"_s, u"Py_nb_power"_s}, + {u"__invert__"_s, u"Py_nb_invert"_s}, + {u"__lshift__"_s, u"Py_nb_lshift"_s}, + {u"__rshift__"_s, u"Py_nb_rshift"_s}, + {u"__and__"_s, u"Py_nb_and"_s}, + {u"__xor__"_s, u"Py_nb_xor"_s}, + {u"__or__"_s, u"Py_nb_or"_s}, + {u"__iadd__"_s, u"Py_nb_inplace_add"_s}, + {u"__isub__"_s, u"Py_nb_inplace_subtract"_s}, + {u"__imul__"_s, u"Py_nb_inplace_multiply"_s}, + {u"__imod__"_s, u"Py_nb_inplace_remainder"_s}, + {u"__ilshift__"_s, u"Py_nb_inplace_lshift"_s}, + {u"__irshift__"_s, u"Py_nb_inplace_rshift"_s}, + {u"__iand__"_s, u"Py_nb_inplace_and"_s}, + {u"__ixor__"_s, u"Py_nb_inplace_xor"_s}, + {u"__ior__"_s, u"Py_nb_inplace_or"_s}, + {u"__bool__"_s, u"Py_nb_bool"_s}, + {u"__int__"_s, u"Py_nb_int"_s}, + {u"__float__"_s, u"Py_nb_float"_s} }; return result; } -void CppGenerator::writeTypeAsNumberDefinition(TextStream &s, const AbstractMetaClass *metaClass) const +void CppGenerator::writeTypeAsNumberDefinition(TextStream &s, const AbstractMetaClassCPtr &metaClass) const { QMap<QString, QString> nb; - const QList<AbstractMetaFunctionCList> opOverloads = - filterGroupedOperatorFunctions(metaClass, - OperatorQueryOption::ArithmeticOp - | OperatorQueryOption::IncDecrementOp - | OperatorQueryOption::LogicalOp - | OperatorQueryOption::BitwiseOp); - - for (const AbstractMetaFunctionCList &opOverload : opOverloads) { + const QList<AbstractMetaFunctionCList> opOverloads = numberProtocolOperators(metaClass); + for (const auto &opOverload : opOverloads) { const auto rfunc = opOverload.at(0); QString opName = ShibokenGenerator::pythonOperatorFunctionName(rfunc); nb[opName] = cpythonFunctionName(rfunc); } + for (auto it = m_nbFuncs.cbegin(), end = m_nbFuncs.cend(); it != end; ++it) { + if (!it.value().isEmpty()) + nb.insert(it.key(), it.value()); + } + QString baseName = cpythonBaseName(metaClass); if (hasBoolCast(metaClass)) - nb.insert(boolT(), baseName + u"___nb_bool"_s); + nb.insert(u"__bool__"_s, baseName + u"___nb_bool"_s); for (auto it = nbFuncs().cbegin(), end = nbFuncs().cend(); it != end; ++it) { const QString &nbName = it.key(); - if (nbName == u"__div__" || nbName == u"__idiv__") - continue; // excludeFromPy3K const auto nbIt = nb.constFind(nbName); - if (nbIt != nb.constEnd()) { - const QString fixednbName = nbName == boolT() - ? u"nb_bool"_s : it.value(); - s << "{Py_" << fixednbName << ", reinterpret_cast<void *>(" - << nbIt.value() << ")},\n"; - } - } - - auto nbIt = nb.constFind(u"__div__"_s); - if (nbIt != nb.constEnd()) - s << "{Py_nb_true_divide, reinterpret_cast<void *>(" << nbIt.value() << ")},\n"; - - nbIt = nb.constFind(u"__idiv__"_s); - if (nbIt != nb.constEnd()) { - s << "// This function is unused in Python 3. We reference it here.\n" - << "{0, reinterpret_cast<void *>(" << nbIt.value() << ")},\n" - << "// This list is ending at the first 0 entry.\n" - << "// Therefore, we need to put the unused functions at the very end.\n"; + if (nbIt != nb.constEnd()) + s << pyTypeSlotEntry(it.value(), nbIt.value()); } } -void CppGenerator::writeTpTraverseFunction(TextStream &s, const AbstractMetaClass *metaClass) +void CppGenerator::writeTpTraverseFunction(TextStream &s, const AbstractMetaClassCPtr &metaClass) { QString baseName = cpythonBaseName(metaClass); s << "static int " << baseName << "_traverse(PyObject *self, visitproc visit, void *arg)\n{\n" << indent - << "return SbkObject_TypeF()->tp_traverse(self, visit, arg);\n" + << "auto traverseProc = " + << pyTypeGetSlot("traverseproc", sbkObjectTypeF, "Py_tp_traverse") << ";\n" + << "return traverseProc(self, visit, arg);\n" << outdent << "}\n"; } -void CppGenerator::writeTpClearFunction(TextStream &s, const AbstractMetaClass *metaClass) +void CppGenerator::writeTpClearFunction(TextStream &s, const AbstractMetaClassCPtr &metaClass) { QString baseName = cpythonBaseName(metaClass); s << "static int " << baseName << "_clear(PyObject *self)\n{\n" << indent - << "return reinterpret_cast<PyTypeObject *>(SbkObject_TypeF())->tp_clear(self);\n" + << "auto clearProc = " + << pyTypeGetSlot("inquiry", sbkObjectTypeF, "Py_tp_clear") << ";\n" + << "return clearProc(self);\n" << outdent << "}\n"; } -void CppGenerator::writeCopyFunction(TextStream &s, const GeneratorContext &context) const +QString CppGenerator::writeCopyFunction(TextStream &s, + TextStream &definitionStream, + TextStream &signatureStream, + const GeneratorContext &context) { - const AbstractMetaClass *metaClass = context.metaClass(); + const auto metaClass = context.metaClass(); const QString className = chopType(cpythonTypeName(metaClass)); - s << "static PyObject *" << className << "___copy__(PyObject *self)\n" - << "{\n" << indent; + const QString funcName = className + u"__copy__"_s; + + signatureStream << fullPythonClassName(metaClass) << ".__copy__()\n"; + definitionStream << PyMethodDefEntry{u"__copy__"_s, funcName, {"METH_NOARGS"_ba}, {}} + << ",\n"; + + s << "static PyObject *" << funcName << "(PyObject *self)\n" + << "{\n" << indent; writeCppSelfDefinition(s, context, ErrorReturn::Default, CppSelfDefinitionFlag::CppSelfAsReference); QString conversionCode; if (!context.forSmartPointer()) @@ -5082,16 +4751,18 @@ void CppGenerator::writeCopyFunction(TextStream &s, const GeneratorContext &cont writeFunctionReturnErrorCheckSection(s, ErrorReturn::Default); s << "return " << PYTHON_RETURN_VAR << ";\n" << outdent << "}\n\n"; + + return funcName; } static inline void writeGetterFunctionStart(TextStream &s, const QString &funcName) { - s << "static PyObject *" << funcName << "(PyObject *self, void *)\n" + s << "static PyObject *" << funcName << "(PyObject *self, void * /* closure */)\n" << "{\n" << indent; } QString CppGenerator::cppFieldAccess(const AbstractMetaField &metaField, - const GeneratorContext &context) const + const GeneratorContext &context) { QString result; QTextStream str(&result); @@ -5105,7 +4776,7 @@ QString CppGenerator::cppFieldAccess(const AbstractMetaField &metaField, void CppGenerator::writeGetterFunction(TextStream &s, const AbstractMetaField &metaField, - const GeneratorContext &context) const + const GeneratorContext &context) { writeGetterFunctionStart(s, cpythonGetterFunctionName(metaField)); @@ -5147,7 +4818,7 @@ void CppGenerator::writeGetterFunction(TextStream &s, << "pyOut = reinterpret_cast<PyObject *>(Shiboken::Object::findColocatedChild(" << "reinterpret_cast<SbkObject *>(self), " << cpythonTypeNameExt(fieldType) << "));\n" - << "if (pyOut) {\n" << indent + << "if (pyOut != nullptr) {\n" << indent << "Py_IncRef(pyOut);\n" << "return pyOut;\n" << outdent << "}\n"; @@ -5159,7 +4830,14 @@ void CppGenerator::writeGetterFunction(TextStream &s, << "Py_IncRef(pyOut);" << "\n" << "return pyOut;" << "\n" << outdent << "}\n"; - // Create and register new wrapper + // Create and register new wrapper. We force a pointer conversion also + // for wrapped value types so that they refer to the struct member, + // avoiding any trouble copying them. Add a parent relationship to + // properly notify if the struct is deleted (see protected_test.py, + // testProtectedValueTypeProperty()). Note that this has currently + // unsolved issues when using temporary Python lists of structs + // which can cause elements to be reported deleted in expressions like + // "foo.list_of_structs[2].field". s << "pyOut = " << "Shiboken::Object::newObject(" << cpythonTypeNameExt(fieldType) << ", " << cppField << ", false, true);\n" @@ -5172,27 +4850,29 @@ void CppGenerator::writeGetterFunction(TextStream &s, } // Write a getter for QPropertySpec -void CppGenerator::writeGetterFunction(TextStream &s, const QPropertySpec &property, - const GeneratorContext &context) const +void CppGenerator::writeGetterFunction(TextStream &s, + const QPropertySpec &property, + const GeneratorContext &context) { writeGetterFunctionStart(s, cpythonGetterFunctionName(property, context.metaClass())); writeCppSelfDefinition(s, context); - const QString value = QStringLiteral("value"); + const QString value = "value"_L1; s << "auto " << value << " = " << CPP_SELF_VAR << "->" << property.read() << "();\n" - << "auto pyResult = "; + << "auto *pyResult = "; writeToPythonConversion(s, property.type(), context.metaClass(), value); - s << ";\nif (PyErr_Occurred() || !pyResult) {\n" << indent - << "Py_XDECREF(pyResult);\nreturn {};\n" << outdent + s << ";\nif (" << shibokenErrorsOccurred << " || pyResult == nullptr) {\n" + << indent << "Py_XDECREF(pyResult);\nreturn {};\n" << outdent << "}\nreturn pyResult;\n" << outdent << "}\n\n"; } // Write setter function preamble (type checks on "pyIn") -void CppGenerator::writeSetterFunctionPreamble(TextStream &s, const QString &name, +void CppGenerator::writeSetterFunctionPreamble(TextStream &s, + const QString &name, const QString &funcName, const AbstractMetaType &type, - const GeneratorContext &context) const + const GeneratorContext &context) { - s << "static int " << funcName << "(PyObject *self, PyObject *pyIn, void *)\n" + s << "static int " << funcName << "(PyObject *self, PyObject *pyIn, void * /* closure */)\n" << "{\n" << indent; writeCppSelfDefinition(s, context, ErrorReturn::Zero); @@ -5214,7 +4894,7 @@ void CppGenerator::writeSetterFunctionPreamble(TextStream &s, const QString &nam void CppGenerator::writeSetterFunction(TextStream &s, const AbstractMetaField &metaField, - const GeneratorContext &context) const + const GeneratorContext &context) { const AbstractMetaType &fieldType = metaField.type(); writeSetterFunctionPreamble(s, metaField.name(), cpythonSetterFunctionName(metaField), @@ -5246,16 +4926,18 @@ void CppGenerator::writeSetterFunction(TextStream &s, } // Write a setter for QPropertySpec -void CppGenerator::writeSetterFunction(TextStream &s, const QPropertySpec &property, - const GeneratorContext &context) const +void CppGenerator::writeSetterFunction(TextStream &s, + const QPropertySpec &property, + const GeneratorContext &context) { - writeSetterFunctionPreamble(s, property.name(), + writeSetterFunctionPreamble(s, + property.name(), cpythonSetterFunctionName(property, context.metaClass()), property.type(), context); s << "auto cppOut = " << CPP_SELF_VAR << "->" << property.read() << "();\n" << PYTHON_TO_CPP_VAR << "(pyIn, &cppOut);\n" - << "if (PyErr_Occurred())\n" << indent + << "if (" << shibokenErrorsOccurred << ")\n" << indent << "return -1;\n" << outdent << CPP_SELF_VAR << "->" << property.write() << "(cppOut);\n" << "return 0;\n" << outdent << "}\n\n"; @@ -5263,7 +4945,7 @@ void CppGenerator::writeSetterFunction(TextStream &s, const QPropertySpec &prope void CppGenerator::writeRichCompareFunctionHeader(TextStream &s, const QString &baseName, - const GeneratorContext &context) const + const GeneratorContext &context) { s << "static PyObject * "; s << baseName << "_richcompare(PyObject *self, PyObject *" << PYTHON_ARG @@ -5275,25 +4957,16 @@ void CppGenerator::writeRichCompareFunctionHeader(TextStream &s, << sbkUnusedVariableCast(PYTHON_TO_CPP_VAR) << '\n'; } -static bool containsGoto(const CodeSnip &s) -{ - return s.code().contains(u"goto"); -} - -static const char richCompareComment[] = - "// PYSIDE-74: By default, we redirect to object's tp_richcompare (which is `==`, `!=`).\n"; - void CppGenerator::writeRichCompareFunction(TextStream &s, const GeneratorContext &context) const { - const AbstractMetaClass *metaClass = context.metaClass(); + const auto metaClass = context.metaClass(); QString baseName = cpythonBaseName(metaClass); writeRichCompareFunctionHeader(s, baseName, context); s << "switch (op) {\n" << indent; const QList<AbstractMetaFunctionCList> &groupedFuncs = filterGroupedOperatorFunctions(metaClass, OperatorQueryOption::ComparisonOp); - bool needErrorLabel = false; for (const AbstractMetaFunctionCList &overloads : groupedFuncs) { const auto rfunc = overloads[0]; @@ -5339,7 +5012,6 @@ void CppGenerator::writeRichCompareFunction(TextStream &s, TypeSystem::TargetLangCode, func, false /* uses PyArgs */, &func->arguments().constLast()); generateOperatorCode = false; - needErrorLabel |= std::any_of(snips.cbegin(), snips.cend(), containsGoto); } } if (generateOperatorCode) { @@ -5373,8 +5045,8 @@ void CppGenerator::writeRichCompareFunction(TextStream &s, << (op == AbstractMetaFunction::OperatorEqual ? "Py_False" : "Py_True") << ";\n" << "Py_INCREF(" << PYTHON_RETURN_VAR << ");\n" << outdent; } else { - s << indent << "goto " << baseName << "_RichComparison_TypeError;\n" << outdent; - needErrorLabel = true; + s << indent << "return Shiboken::returnFromRichCompare(" + << PYTHON_RETURN_VAR << ");\n" << outdent; } s << "}\n\n"; @@ -5383,131 +5055,9 @@ void CppGenerator::writeRichCompareFunction(TextStream &s, s << "default:\n" << indent << richCompareComment << "return FallbackRichCompare(self, " << PYTHON_ARG << ", op);\n" - << outdent << outdent << "}\n\n"; - - writeRichCompareFunctionFooter(s, baseName, needErrorLabel); -} - -void CppGenerator::writeRichCompareFunctionFooter(TextStream &s, - const QString &baseName, - bool writeErrorLabel) -{ - s << "if (" << PYTHON_RETURN_VAR << " && !PyErr_Occurred())\n" << indent - << "return " << PYTHON_RETURN_VAR << ";\n" << outdent; - if (writeErrorLabel) - s << baseName << "_RichComparison_TypeError:\n"; - s << "Shiboken::Errors::setOperatorNotImplemented();\n" - << ErrorReturn::Default << '\n' << outdent << "}\n\n"; -} - -using ComparisonOperatorList = QList<AbstractMetaFunction::ComparisonOperatorType>; - -// Return the available comparison operators for smart pointers -static ComparisonOperatorList smartPointeeComparisons(const GeneratorContext &context) -{ - Q_ASSERT(context.forSmartPointer()); - auto te = context.preciseType().instantiations().constFirst().typeEntry(); - if (isExtendedCppPrimitive(te)) { // Primitive pointee types have all - return {AbstractMetaFunction::OperatorEqual, - AbstractMetaFunction::OperatorNotEqual, - AbstractMetaFunction::OperatorLess, - AbstractMetaFunction::OperatorLessEqual, - AbstractMetaFunction::OperatorGreater, - AbstractMetaFunction::OperatorGreaterEqual}; - } - - auto *pointeeClass = context.pointeeClass(); - if (!pointeeClass) - return {}; - - ComparisonOperatorList result; - const auto &comparisons = - pointeeClass->operatorOverloads(OperatorQueryOption::SymmetricalComparisonOp); - for (const auto &f : comparisons) { - const auto ct = f->comparisonOperatorType().value(); - if (!result.contains(ct)) - result.append(ct); - } - return result; -} - -void CppGenerator::writeSmartPointerRichCompareFunction(TextStream &s, - const GeneratorContext &context) const -{ - static const char selfPointeeVar[] = "cppSelfPointee"; - static const char cppArg0PointeeVar[] = "cppArg0Pointee"; - - const AbstractMetaClass *metaClass = context.metaClass(); - QString baseName = cpythonBaseName(metaClass); - writeRichCompareFunctionHeader(s, baseName, context); - - s << "if ("; - writeTypeCheck(s, context.preciseType(), PYTHON_ARG); - s << ") {\n" << indent; - writeArgumentConversion(s, context.preciseType(), CPP_ARG0, - PYTHON_ARG, ErrorReturn::Default, metaClass); - - const auto te = context.preciseType().typeEntry(); - Q_ASSERT(te->isSmartPointer()); - const auto ste = qSharedPointerCast<const SmartPointerTypeEntry>(te); - - s << "const auto *" << selfPointeeVar << " = " << CPP_SELF_VAR - << '.' << ste->getter() << "();\n"; - s << "const auto *" << cppArg0PointeeVar << " = " << CPP_ARG0 - << '.' << ste->getter() << "();\n"; - - // If we have an object without any comparisons, only generate a simple - // equality check by pointee address - auto availableOps = smartPointeeComparisons(context); - const bool comparePointeeAddressOnly = availableOps.isEmpty(); - if (comparePointeeAddressOnly) { - availableOps << AbstractMetaFunction::OperatorEqual - << AbstractMetaFunction::OperatorNotEqual; - } else { - // For value types with operators, we complain about nullptr - s << "if (" << selfPointeeVar << " == nullptr || " << cppArg0PointeeVar - << " == nullptr) {\n" << indent - << "PyErr_SetString(PyExc_NotImplementedError, \"nullptr passed to comparison.\");\n" - << ErrorReturn::Default << '\n' << outdent << "}\n"; - } - - s << "bool " << CPP_RETURN_VAR << "= false;\n" - << "switch (op) {\n"; - for (auto op : availableOps) { - s << "case " << AbstractMetaFunction::pythonRichCompareOpCode(op) << ":\n" - << indent << CPP_RETURN_VAR << " = "; - if (comparePointeeAddressOnly) { - s << selfPointeeVar << ' ' << AbstractMetaFunction::cppComparisonOperator(op) - << ' ' << cppArg0PointeeVar << ";\n"; - } else { - // Shortcut for equality: Check pointee address - if (op == AbstractMetaFunction::OperatorEqual - || op == AbstractMetaFunction::OperatorLessEqual - || op == AbstractMetaFunction::OperatorGreaterEqual) { - s << selfPointeeVar << " == " << cppArg0PointeeVar << " || "; - } - // Generate object's comparison - s << "*" << selfPointeeVar << ' ' - << AbstractMetaFunction::cppComparisonOperator(op) << " *" - << cppArg0PointeeVar << ";\n"; - } - s << "break;\n" << outdent; - - } - if (availableOps.size() < 6) { - s << "default:\n" << indent - << richCompareComment - << "return FallbackRichCompare(self, " << PYTHON_ARG << ", op);\n" << outdent; - } - s << "}\n" << PYTHON_RETURN_VAR << " = " << CPP_RETURN_VAR - << " ? Py_True : Py_False;\n" - << "Py_INCREF(" << PYTHON_RETURN_VAR << ");\n"; - - s << outdent << "} else {\n" << indent - << "goto " << baseName << "_RichComparison_TypeError;\n" - << outdent << "}\n"; - - writeRichCompareFunctionFooter(s, baseName, true); + << outdent << outdent << "}\n\n" + << "return Shiboken::returnFromRichCompare(" << PYTHON_RETURN_VAR << ");\n" << outdent + << "}\n\n"; } // Return a flag combination for PyMethodDef @@ -5528,9 +5078,9 @@ QByteArrayList CppGenerator::methodDefinitionParameters(const OverloadData &over } // METH_STATIC causes a crash when used for global functions (also from // invisible namespaces). - auto *ownerClass = overloadData.referenceFunction()->ownerClass(); + const auto ownerClass = overloadData.referenceFunction()->ownerClass(); if (ownerClass - && !invisibleTopNamespaces().contains(const_cast<AbstractMetaClass *>(ownerClass))) { + && !invisibleTopNamespaces().contains(std::const_pointer_cast<AbstractMetaClass>(ownerClass))) { if (overloadData.hasStaticFunction()) result.append(QByteArrayLiteral("METH_STATIC")); if (overloadData.hasClassMethod()) @@ -5572,10 +5122,15 @@ QString CppGenerator::signatureParameter(const AbstractMetaArgument &arg) const 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 << f->arguments().constFirst().type().pythonSignature(); + } + } else if (f->isConversionOperator()) { signatures << f->ownerClass()->fullName(); + } } const qsizetype size = signatures.size(); @@ -5589,12 +5144,6 @@ QString CppGenerator::signatureParameter(const AbstractMetaArgument &arg) const if (size > 1) s << ']'; - if (!arg.defaultValueExpression().isEmpty()) { - s << '='; - QString e = arg.defaultValueExpression(); - e.replace(u"::"_s, u"."_s); - s << e; - } return result; } @@ -5611,18 +5160,22 @@ void CppGenerator::writeSignatureInfo(TextStream &s, const OverloadData &overloa // PYSIDE-1328: `self`-ness cannot be computed in Python because there are mixed cases. // Toplevel functions like `PySide6.QtCore.QEnum` are always self-less. if (!(f->isStatic()) && f->ownerClass()) - args << u"self"_s; + args << PYTHON_SELF_VAR; const auto &arguments = f->arguments(); for (qsizetype i = 0, size = arguments.size(); i < size; ++i) { const auto n = i + 1; + const auto &arg = arguments.at(i); if (!f->argumentRemoved(n)) { QString t = f->pyiTypeReplaced(n); if (t.isEmpty()) { - t = signatureParameter(arguments.at(i)); + t = signatureParameter(arg); } else { t.prepend(u':'); - t.prepend(arguments.at(i).name()); + t.prepend(arg.name()); } + QString defaultValue = arg.defaultValueExpression(); + if (!defaultValue.isEmpty()) + t += u'=' + defaultValue.replace(u"::"_s, u"."_s); args.append(t); } } @@ -5642,43 +5195,44 @@ void CppGenerator::writeSignatureInfo(TextStream &s, const OverloadData &overloa } } -void CppGenerator::writeEnumsInitialization(TextStream &s, AbstractMetaEnumList &enums, - ErrorReturn errorReturn) const +void CppGenerator::writeEnumsInitialization(TextStream &s, AbstractMetaEnumList &enums) { if (enums.isEmpty()) return; - bool preambleWrittenE = false; - bool preambleWrittenF = false; + bool preambleWritten = false; + bool etypeUsed = false; + for (const AbstractMetaEnum &cppEnum : std::as_const(enums)) { if (cppEnum.isPrivate()) continue; - if (!preambleWrittenE) { + if (!preambleWritten) { s << "// Initialization of enums.\n" + << "Shiboken::AutoDecRef tpDict{};\n" << "PyTypeObject *EType{};\n\n"; - preambleWrittenE = true; + preambleWritten = true; } - if (!preambleWrittenF && cppEnum.typeEntry()->flags()) { - s << "// Initialization of enums, flags part.\n" - << "PyTypeObject *FType{};\n\n"; - preambleWrittenF = true; - } - writeEnumInitialization(s, cppEnum, errorReturn); + ConfigurableScope configScope(s, cppEnum.typeEntry()); + etypeUsed |= writeEnumInitialization(s, cppEnum); } + if (preambleWritten && !etypeUsed) + s << sbkUnusedVariableCast("EType"); } -static QString mangleName(QString name) +static qsizetype maxLineLength(const QStringList &list) { - if (name == u"None" || name == u"False" || name == u"True") - name += u'_'; - return name; + qsizetype result = 0; + for (const auto &s : list) { + if (auto len = s.size(); len > result) + result = len; + } + return result; } -void CppGenerator::writeEnumInitialization(TextStream &s, const AbstractMetaEnum &cppEnum, - ErrorReturn errorReturn) const +bool CppGenerator::writeEnumInitialization(TextStream &s, const AbstractMetaEnum &cppEnum) { - const AbstractMetaClass *enclosingClass = cppEnum.targetLangEnclosingClass(); - bool hasUpperEnclosingClass = enclosingClass - && enclosingClass->targetLangEnclosingClass() != nullptr; + const auto enclosingClass = cppEnum.targetLangEnclosingClass(); + const bool hasUpperEnclosingClass = enclosingClass + && enclosingClass->targetLangEnclosingClass(); EnumTypeEntryCPtr enumTypeEntry = cppEnum.typeEntry(); QString enclosingObjectVariable; if (enclosingClass) @@ -5692,96 +5246,115 @@ void CppGenerator::writeEnumInitialization(TextStream &s, const AbstractMetaEnum s << (cppEnum.isAnonymous() ? "anonymous enum identified by enum value" : "enum"); s << " '" << cppEnum.name() << "'.\n"; - QString enumVarTypeObj = cpythonTypeNameExt(enumTypeEntry); - if (!cppEnum.isAnonymous()) { - int packageLevel = packageName().count(u'.') + 1; - FlagsTypeEntryPtr flags = enumTypeEntry->flags(); - if (!flags.isNull()) { - // The following could probably be made nicer: - // We need 'flags->flagsName()' with the full module/class path. - QString fullPath = getClassTargetFullName(cppEnum); - fullPath.truncate(fullPath.lastIndexOf(u'.') + 1); - s << "FType = PySide::QFlags::create(\"" - << packageLevel << ':' << fullPath << flags->flagsName() << "\", \n" << indent - << cpythonEnumName(cppEnum) << "_number_slots);\n" << outdent - << cpythonTypeNameExt(flags) << " = FType;\n"; + const QString userType = cppEnum.typeEntry()->cppType(); + const bool isSigned = cppEnum.isSigned() && !userType.contains(u"unsigned"_s); + const bool isAccessible = !avoidProtectedHack() || !cppEnum.isProtected(); + const auto enumValues = cppEnum.nonRejectedValues(); + + const QString prefix = cppEnum.name(); + + const QString intType = userType.isEmpty() ? cppEnum.underlyingType() : userType; + + // Create a list of values + const QString initializerValues = prefix + u"_InitializerValues"_s; + const QString initializerName = prefix + u"_Initializer"_s; + + // Build maybe array of enum names. + if (cppEnum.enumKind() != AnonymousEnum) { + s << "const char *" << initializerName << "[] = {\n" << indent; + for (const auto &enumValue : enumValues) { + QString name = mangleName(enumValue.name()); + s << '\"' << name << "\",\n"; } + s << "nullptr};\n" << outdent; + } - s << "EType = Shiboken::Enum::" - << ((enclosingClass - || hasUpperEnclosingClass) ? "createScopedEnum" : "createGlobalEnum") - << '(' << enclosingObjectVariable << ',' << '\n' << indent - << '"' << cppEnum.name() << "\",\n" - << '"' << packageLevel << ':' << getClassTargetFullName(cppEnum) << "\",\n" - << '"' << cppEnum.qualifiedCppName() << '"'; - if (flags) - s << ",\nFType"; - s << ");\n" << outdent - << "if (!EType)\n" - << indent << errorReturn << outdent << '\n'; - } - - for (const AbstractMetaEnumValue &enumValue : cppEnum.values()) { - if (enumTypeEntry->isEnumValueRejected(enumValue.name())) - continue; + int targetHexLen = 0; + QString usedIntType = userType; + if (usedIntType.isEmpty()) { + const int usedBits = cppEnum.usedBits(); + targetHexLen = usedBits / 4; + usedIntType = AbstractMetaEnum::intTypeForSize(usedBits, cppEnum.isSigned()); + } - QString enumValueText; - if (!avoidProtectedHack() || !cppEnum.isProtected()) { - enumValueText = cppEnum.typeEntry()->cppType(); - if (enumValueText.isEmpty()) - enumValueText = u"Shiboken::Enum::EnumValueType"_s; - enumValueText += u'('; - if (cppEnum.enclosingClass()) - enumValueText += cppEnum.enclosingClass()->qualifiedCppName() + u"::"_s; - // Fully qualify the value which is required for C++ 11 enum classes. - if (!cppEnum.isAnonymous()) - enumValueText += cppEnum.name() + u"::"_s; - enumValueText += enumValue.name(); - enumValueText += u')'; - } else { - enumValueText += enumValue.value().toString(); + if (usedIntType != intType) + s << "// \"" << usedIntType << "\" used instead of \"" << intType << "\"\n"; + + // Calculating formatting columns + QString enumValuePrefix; + if (isAccessible) { + if (cppEnum.enclosingClass()) + enumValuePrefix += cppEnum.enclosingClass()->qualifiedCppName() + u"::"_s; + if (!cppEnum.isAnonymous()) + enumValuePrefix += cppEnum.name() + u"::"_s; + } + + // Build array of enum values + if (enumValues.isEmpty()) { + s << "const " << usedIntType << " *" << initializerValues << "{};\n"; + } else { + QStringList values; + values.reserve(enumValues.size()); + s << "constexpr " << usedIntType << ' ' << initializerValues << "[] = {\n" << indent; + for (qsizetype idx = 0, last = enumValues.size() - 1; idx <= last; ++idx) { + const auto &enumValue = enumValues.at(idx); + QString line = usedIntType + u'(' + (isAccessible + ? enumValuePrefix + enumValue.name() + : enumValue.value().toString()) + u')'; + if (idx != last) + line += u','; + values.append(line); } - const QString mangledName = mangleName(enumValue.name()); - switch (cppEnum.enumKind()) { - case AnonymousEnum: + const auto len = maxLineLength(values) + 1; + for (qsizetype idx = 0, size = enumValues.size(); idx < size; ++idx) { + const auto &enumValue = enumValues.at(idx).value(); + const char *numberSpace = enumValue.isNegative() ? " " : " "; + s << values.at(idx) << Pad(' ', len - values.at(idx).size()) + << "//" << numberSpace << enumValue.toHex(targetHexLen) + << numberSpace << enumValue.toString() << '\n'; + } + s << "};\n" << outdent; + } + + // Build initialization of anonymous enums + if (cppEnum.enumKind() == AnonymousEnum) { + int idx = 0; + for (const auto &enumValue : enumValues) { + const QString mangledName = mangleName(enumValue.name()); + const QString pyValue = initializerValues + u'[' + QString::number(idx++) + u']'; if (enclosingClass || hasUpperEnclosingClass) { - s << "{\n" << indent - << "PyObject *anonEnumItem = PyLong_FromLong(" << enumValueText << ");\n" - << "if (PyDict_SetItemString(reinterpret_cast<PyTypeObject *>(" - << enclosingObjectVariable - << ")->tp_dict, \"" << mangledName << "\", anonEnumItem) < 0)\n" - << indent << errorReturn << outdent - << "Py_DECREF(anonEnumItem);\n" << outdent - << "}\n"; + s << "tpDict.reset(PepType_GetDict(reinterpret_cast<PyTypeObject *>(" + << enclosingObjectVariable << ")));\n" + << "PyDict_SetItemString(tpDict.object(), \"" << mangledName << "\",\n" + << indent << (isSigned ? "PyLong_FromLongLong" : "PyLong_FromUnsignedLongLong") + << "(" << pyValue << "));\n" << outdent; } else { - s << "if (PyModule_AddIntConstant(module, \"" << mangledName << "\", "; - s << enumValueText << ") < 0)\n" << indent << errorReturn << outdent; + s << "PyModule_AddObject(module, \"" << mangledName << "\",\n" << indent + << (isSigned ? "PyLong_FromLongLong" : "PyLong_FromUnsignedLongLong") << "(" + << pyValue << "));\n" << outdent; } - break; - case CEnum: - s << "if (!Shiboken::Enum::"; - s << ((enclosingClass || hasUpperEnclosingClass) ? "createScopedEnumItem" - : "createGlobalEnumItem"); - s << '(' << "EType" << ',' << '\n' << indent - << enclosingObjectVariable << ", \"" << mangledName << "\", " - << enumValueText << "))\n" << errorReturn << outdent; - break; - case EnumClass: - s << "if (!Shiboken::Enum::createScopedEnumItem(" - << "EType" << ",\n" << indent - << "EType" << ", \"" << mangledName << "\", " - << enumValueText << "))\n" << errorReturn << outdent; - break; } } - s << "// PYSIDE-1735: Resolving the whole enum class at the end for API compatibility.\n" - << "EType = morphLastEnumToPython();\n" - << enumVarTypeObj << " = EType;\n"; + + bool etypeUsed = false; + + QString enumVarTypeObj = cpythonTypeNameExtSet(enumTypeEntry); + if (!cppEnum.isAnonymous()) { + int packageLevel = packageName().count(u'.') + 1; + s << "EType = Shiboken::Enum::" + << "createPythonEnum" + << '(' << enclosingObjectVariable << ",\n" << indent + << '"' << packageLevel << ':' << getClassTargetFullName(cppEnum) << "\",\n" + << initializerName << ", " << initializerValues << ");\n" << outdent + << enumVarTypeObj << " = EType;\n"; + etypeUsed = true; + } + if (cppEnum.typeEntry()->flags()) { s << "// PYSIDE-1735: Mapping the flags class to the same enum class.\n" - << cpythonTypeNameExt(cppEnum.typeEntry()->flags()) << " =\n" - << indent << "mapFlagsToSameEnum(FType, EType);\n" << outdent; + << cpythonTypeNameExtSet(cppEnum.typeEntry()->flags()) << " =\n" + << indent << "EType;\n" << outdent; } writeEnumConverterInitialization(s, cppEnum); @@ -5789,9 +5362,11 @@ void CppGenerator::writeEnumInitialization(TextStream &s, const AbstractMetaEnum if (cppEnum.typeEntry()->flags()) s << "/flags"; s << ".\n\n"; + + return etypeUsed; } -void CppGenerator::writeSignalInitialization(TextStream &s, const AbstractMetaClass *metaClass) +void CppGenerator::writeSignalInitialization(TextStream &s, const AbstractMetaClassCPtr &metaClass) { // Try to check something and print some warnings const auto &signalFuncs = metaClass->cppSignalFunctions(); @@ -5813,136 +5388,11 @@ void CppGenerator::writeSignalInitialization(TextStream &s, const AbstractMetaCl } } - s << "PySide::Signal::registerSignals(pyType, &::" + s << "PySide::Signal::registerSignals(pyType, &" << m_gsp << metaClass->qualifiedCppName() << "::staticMetaObject);\n"; } -void CppGenerator::writeFlagsToLong(TextStream &s, const AbstractMetaEnum &cppEnum) -{ - FlagsTypeEntryPtr flagsEntry = cppEnum.typeEntry()->flags(); - if (!flagsEntry) - return; - s << "static PyObject *" << cpythonEnumName(cppEnum) << "_long(PyObject *self)\n" - << "{\n" << indent - << "int val;\n"; - AbstractMetaType flagsType = AbstractMetaType::fromTypeEntry(flagsEntry); - s << cpythonToCppConversionFunction(flagsType) << "self, &val);\n" - << "return Shiboken::Conversions::copyToPython(Shiboken::Conversions::PrimitiveTypeConverter<int>(), &val);\n" - << outdent << "}\n"; -} - -void CppGenerator::writeFlagsNonZero(TextStream &s, const AbstractMetaEnum &cppEnum) -{ - FlagsTypeEntryPtr flagsEntry = cppEnum.typeEntry()->flags(); - if (flagsEntry.isNull()) - return; - s << "static int " << cpythonEnumName(cppEnum) << "__nonzero(PyObject *self)\n"; - s << "{\n" << indent << "int val;\n"; - AbstractMetaType flagsType = AbstractMetaType::fromTypeEntry(flagsEntry); - s << cpythonToCppConversionFunction(flagsType) << "self, &val);\n" - << "return val != 0;\n" - << outdent << "}\n"; -} - -void CppGenerator::writeFlagsMethods(TextStream &s, const AbstractMetaEnum &cppEnum) -{ - writeFlagsBinaryOperator(s, cppEnum, u"and"_s, u"&"_s); - writeFlagsBinaryOperator(s, cppEnum, u"or"_s, u"|"_s); - writeFlagsBinaryOperator(s, cppEnum, u"xor"_s, u"^"_s); - - writeFlagsUnaryOperator(s, cppEnum, u"invert"_s, u"~"_s); - writeFlagsToLong(s, cppEnum); - writeFlagsNonZero(s, cppEnum); - - s << '\n'; -} - -void CppGenerator::writeFlagsNumberMethodsDefinition(TextStream &s, const AbstractMetaEnum &cppEnum) -{ - QString cpythonName = cpythonEnumName(cppEnum); - - s << "static PyType_Slot " << cpythonName << "_number_slots[] = {\n" << indent - << "{Py_nb_bool, reinterpret_cast<void *>(" << cpythonName << "__nonzero)},\n" - << "{Py_nb_invert, reinterpret_cast<void *>(" << cpythonName << "___invert__)},\n" - << "{Py_nb_and, reinterpret_cast<void *>(" << cpythonName << "___and__)},\n" - << "{Py_nb_xor, reinterpret_cast<void *>(" << cpythonName << "___xor__)},\n" - << "{Py_nb_or, reinterpret_cast<void *>(" << cpythonName << "___or__)},\n" - << "{Py_nb_int, reinterpret_cast<void *>(" << cpythonName << "_long)},\n" - << "{Py_nb_index, reinterpret_cast<void *>(" << cpythonName << "_long)},\n" - << "{0, " << NULL_PTR << "} // sentinel\n" << outdent - << "};\n\n"; -} - -void CppGenerator::writeFlagsNumberMethodsDefinitions(TextStream &s, - const AbstractMetaEnumList &enums) -{ - for (const AbstractMetaEnum &e : enums) { - if (!e.isAnonymous() && !e.isPrivate() && e.typeEntry()->flags()) { - writeFlagsMethods(s, e); - writeFlagsNumberMethodsDefinition(s, e); - s << '\n'; - } - } -} - -void CppGenerator::writeFlagsBinaryOperator(TextStream &s, const AbstractMetaEnum &cppEnum, - const QString &pyOpName, const QString &cppOpName) -{ - FlagsTypeEntryPtr flagsEntry = cppEnum.typeEntry()->flags(); - Q_ASSERT(!flagsEntry.isNull()); - - s << "PyObject *" << cpythonEnumName(cppEnum) << "___" << pyOpName - << "__(PyObject *self, PyObject *" << PYTHON_ARG << ")\n{\n" << indent; - - AbstractMetaType flagsType = AbstractMetaType::fromTypeEntry(flagsEntry); - s << "::" << flagsEntry->originalName() << " cppResult, " << CPP_SELF_VAR - << ", cppArg;\n" - << CPP_SELF_VAR << " = static_cast<::" << flagsEntry->originalName() - << ">(int(PyLong_AsLong(self)));\n" - // PYSIDE-1436: Need to error check self as well because operators are used - // sometimes with swapped args. - << "if (PyErr_Occurred())\n" << indent - << "return nullptr;\n" << outdent - << "cppArg = static_cast<" << flagsEntry->originalName() - << ">(int(PyLong_AsLong(" << PYTHON_ARG << ")));\n" - << "if (PyErr_Occurred())\n" << indent - << "return nullptr;\n" << outdent - << "cppResult = " << CPP_SELF_VAR << " " << cppOpName << " cppArg;\n" - << "return "; - writeToPythonConversion(s, flagsType, nullptr, u"cppResult"_s); - s << ";\n" << outdent << "}\n\n"; -} - -void CppGenerator::writeFlagsUnaryOperator(TextStream &s, const AbstractMetaEnum &cppEnum, - const QString &pyOpName, - const QString &cppOpName, bool boolResult) -{ - FlagsTypeEntryPtr flagsEntry = cppEnum.typeEntry()->flags(); - Q_ASSERT(flagsEntry); - - s << "PyObject *" << cpythonEnumName(cppEnum) << "___" << pyOpName - << "__(PyObject *self, PyObject *" << PYTHON_ARG << ")\n{\n" << indent; - if (cppOpName == u"~") - s << sbkUnusedVariableCast(PYTHON_ARG); - - AbstractMetaType flagsType = AbstractMetaType::fromTypeEntry(flagsEntry); - s << "::" << flagsEntry->originalName() << " " << CPP_SELF_VAR << ";\n" - << cpythonToCppConversionFunction(flagsType) << "self, &" << CPP_SELF_VAR - << ");\n"; - if (boolResult) - s << "bool"; - else - s << "::" << flagsEntry->originalName(); - s << " cppResult = " << cppOpName << CPP_SELF_VAR << ";\n" - << "return "; - if (boolResult) - s << "PyBool_FromLong(cppResult)"; - else - writeToPythonConversion(s, flagsType, nullptr, u"cppResult"_s); - s << ";\n" << outdent << "}\n\n"; -} - -QString CppGenerator::getSimpleClassInitFunctionName(const AbstractMetaClass *metaClass) +QString CppGenerator::getSimpleClassInitFunctionName(const AbstractMetaClassCPtr &metaClass) { QString initFunctionName; // Disambiguate namespaces per module to allow for extending them. @@ -5953,7 +5403,8 @@ QString CppGenerator::getSimpleClassInitFunctionName(const AbstractMetaClass *me return initFunctionName; } -QString CppGenerator::getSimpleClassStaticFieldsInitFunctionName(const AbstractMetaClass *metaClass) +QString + CppGenerator::getSimpleClassStaticFieldsInitFunctionName(const AbstractMetaClassCPtr &metaClass) { return u"init_"_s + getSimpleClassInitFunctionName(metaClass) + u"StaticFields"_s; @@ -5984,8 +5435,8 @@ void CppGenerator::writeSignatureStrings(TextStream &s, } // Return the class name for which to invoke the destructor -QString CppGenerator::destructorClassName(const AbstractMetaClass *metaClass, - const GeneratorContext &classContext) const +QString CppGenerator::destructorClassName(const AbstractMetaClassCPtr &metaClass, + const GeneratorContext &classContext) { if (metaClass->isNamespace() || metaClass->hasPrivateDestructor()) return {}; @@ -6002,14 +5453,69 @@ QString CppGenerator::destructorClassName(const AbstractMetaClass *metaClass, return metaClass->qualifiedCppName(); } +// Return the base type entries for introduceWrapperType() +static ComplexTypeEntryCList pyBaseTypeEntries(const AbstractMetaClassCPtr &metaClass) +{ + ComplexTypeEntryCList result; + if (metaClass->isNamespace()) { + if (auto extended = metaClass->extendedNamespace()) + result.append(extended->typeEntry()); + return result; + } + + const auto &baseClasses = metaClass->typeSystemBaseClasses(); + for (auto base : baseClasses) { + for (; base != nullptr; base = base->baseClass()) { // Find a type that is not disabled. + const auto ct = base->typeEntry()->codeGeneration(); + if (ct == TypeEntry::GenerateCode || ct == TypeEntry::GenerateForSubclass) + break; + } + result.append(base->typeEntry()); + } + return result; +} + +// Return the base type strings for introduceWrapperType() +QStringList CppGenerator::pyBaseTypes(const AbstractMetaClassCPtr &metaClass) +{ + const auto &baseEntries = pyBaseTypeEntries(metaClass); + QStringList result; + for (const auto &baseEntry : baseEntries) + result.append("reinterpret_cast<PyObject *>("_L1 + cpythonTypeNameExt(baseEntry) + u')'); + if (result.isEmpty()) // no base classes -> SbkObjectType. + result.append(sbkObjectTypeF); + return result; +} + +void CppGenerator::writeInitInheritance(TextStream &s) const +{ + s << "static void " << initInheritanceFunction << "()\n{\n" << indent + << "auto &bm = Shiboken::BindingManager::instance();\n" + << sbkUnusedVariableCast("bm"); + for (const auto &cls : api().classes()){ + auto te = cls->typeEntry(); + if (shouldGenerate(te)) { + const auto &baseEntries = pyBaseTypeEntries(cls); + if (!baseEntries.isEmpty()) { + const QString childTypeInitStruct = typeInitStruct(cls->typeEntry()); + for (const auto &baseEntry : baseEntries) { + s << "bm.addClassInheritance(&" << typeInitStruct(baseEntry) << ",\n" + << Pad(' ', 23) << '&' << childTypeInitStruct << ");\n"; + } + } + } + } + s << outdent << "}\n\n"; +} + void CppGenerator::writeClassRegister(TextStream &s, - const AbstractMetaClass *metaClass, + const AbstractMetaClassCPtr &metaClass, const GeneratorContext &classContext, const QString &signatures) const { ComplexTypeEntryCPtr classTypeEntry = metaClass->typeEntry(); - const AbstractMetaClass *enc = metaClass->targetLangEnclosingClass(); + AbstractMetaClassCPtr enc = metaClass->targetLangEnclosingClass(); QString enclosingObjectVariable = enc ? u"enclosingClass"_s : u"module"_s; QString pyTypeName = cpythonTypeName(metaClass); @@ -6017,23 +5523,26 @@ void CppGenerator::writeClassRegister(TextStream &s, // PYSIDE-510: Create a signatures string for the introspection feature. writeSignatureStrings(s, signatures, initFunctionName, "functions"); - s << "void init_" << initFunctionName; - s << "(PyObject *" << enclosingObjectVariable << ")\n{\n" << indent; + s << "PyTypeObject *init_" << initFunctionName + << "(PyObject *" << enclosingObjectVariable << ")\n{\n" << indent; + + const QString globalTypeVarExpr = !classContext.forSmartPointer() + ? cpythonTypeNameExtSet(classTypeEntry) + : cpythonTypeNameExtSet(classContext.preciseType()); + s << "if (" << globalTypeVarExpr << " != nullptr)\n" << indent + << "return " << globalTypeVarExpr << ";\n\n" << outdent; // Multiple inheritance QString pyTypeBasesVariable = chopType(pyTypeName) + u"_Type_bases"_s; - const auto &baseClasses = metaClass->typeSystemBaseClasses(); - if (metaClass->baseClassNames().size() > 1) { - s << "PyObject *" << pyTypeBasesVariable - << " = PyTuple_Pack(" << baseClasses.size() << ',' << '\n' << indent; - for (qsizetype i = 0, size = baseClasses.size(); i < size; ++i) { - if (i) - s << ",\n"; - s << "reinterpret_cast<PyObject *>(" - << cpythonTypeNameExt(baseClasses.at(i)->typeEntry()) << ')'; - } - s << ");\n\n" << outdent; + const QStringList pyBases = pyBaseTypes(metaClass); + s << "Shiboken::AutoDecRef " << pyTypeBasesVariable << "(PyTuple_Pack(" + << pyBases.size() << ",\n" << indent; + for (qsizetype i = 0, size = pyBases.size(); i < size; ++i) { + if (i) + s << ",\n"; + s << pyBases.at(i); } + s << "));\n\n" << outdent; // Create type and insert it in the module or enclosing class. const QString typePtr = u"_"_s + chopType(pyTypeName) @@ -6065,29 +5574,11 @@ void CppGenerator::writeClassRegister(TextStream &s, if (dtorClassName.isEmpty()) s << "nullptr,\n"; else - s << "&Shiboken::callCppDestructor< ::" << dtorClassName << " >,\n"; - - // 6:baseType: Find a type that is not disabled. - auto base = metaClass->isNamespace() - ? metaClass->extendedNamespace() : metaClass->baseClass(); - if (!metaClass->isNamespace()) { - for (; base != nullptr; base = base->baseClass()) { - const auto ct = base->typeEntry()->codeGeneration(); - if (ct == TypeEntry::GenerateCode || ct == TypeEntry::GenerateForSubclass) - break; - } - } - if (base) { - s << cpythonTypeNameExt(base->typeEntry()) << ",\n"; - } else { - s << "0,\n"; - } + s << "&Shiboken::callCppDestructor< " << globalScopePrefix(classContext) + << dtorClassName << " >,\n"; // 7:baseTypes - if (metaClass->baseClassNames().size() > 1) - s << pyTypeBasesVariable << ',' << '\n'; - else - s << "0,\n"; + s << pyTypeBasesVariable << ".object()," << '\n'; // 8:wrapperflags QByteArrayList wrapperFlags; @@ -6095,6 +5586,8 @@ void CppGenerator::writeClassRegister(TextStream &s, wrapperFlags.append(QByteArrayLiteral("Shiboken::ObjectType::WrapperFlags::InnerClass")); if (metaClass->deleteInMainThread()) wrapperFlags.append(QByteArrayLiteral("Shiboken::ObjectType::WrapperFlags::DeleteInMainThread")); + if (classTypeEntry->isValue()) + wrapperFlags.append("Shiboken::ObjectType::WrapperFlags::Value"_ba); if (wrapperFlags.isEmpty()) s << '0'; else @@ -6107,11 +5600,7 @@ void CppGenerator::writeClassRegister(TextStream &s, if (usePySideExtensions() && !classContext.forSmartPointer()) s << "SbkObjectType_SetPropertyStrings(pyType, " << chopType(pyTypeName) << "_PropertyStrings);\n"; - - if (!classContext.forSmartPointer()) - s << cpythonTypeNameExt(classTypeEntry) << " = pyType;\n\n"; - else - s << cpythonTypeNameExt(classContext.preciseType()) << " = pyType;\n\n"; + s << globalTypeVarExpr << " = pyType;\n\n"; // Register conversions for the type. writeConverterRegister(s, metaClass, classContext); @@ -6126,7 +5615,7 @@ void CppGenerator::writeClassRegister(TextStream &s, } // Fill multiple inheritance data, if needed. - const AbstractMetaClass *miClass = getMultipleInheritingClass(metaClass); + const auto miClass = getMultipleInheritingClass(metaClass); if (miClass) { s << "MultipleInheritanceInitFunction func = "; if (miClass == metaClass) { @@ -6143,8 +5632,9 @@ void CppGenerator::writeClassRegister(TextStream &s, // Set typediscovery struct or fill the struct of another one if (needsTypeDiscoveryFunction(metaClass)) { - s << "Shiboken::ObjectType::setTypeDiscoveryFunctionV2(" << cpythonTypeName(metaClass) - << ", &" << cpythonBaseName(metaClass) << "_typeDiscovery);\n\n"; + s << "Shiboken::ObjectType::setTypeDiscoveryFunctionV2(\n" << indent + << cpythonTypeName(metaClass) + << ", &" << cpythonBaseName(metaClass) << "_typeDiscovery);" << outdent << "\n\n"; } AbstractMetaEnumList classEnums = metaClass->enums(); @@ -6154,7 +5644,7 @@ void CppGenerator::writeClassRegister(TextStream &s, s << "// Pass the ..._EnumFlagInfo to the class.\n" << "SbkObjectType_SetEnumFlagInfo(pyType, " << chopType(pyTypeName) << "_EnumFlagInfo);\n\n"; - writeEnumsInitialization(s, classEnums, ErrorReturn::Void); + writeEnumsInitialization(s, classEnums); if (metaClass->hasSignals()) writeSignalInitialization(s, metaClass); @@ -6174,25 +5664,40 @@ void CppGenerator::writeClassRegister(TextStream &s, writeInitQtMetaTypeFunctionBody(s, classContext); } - if (usePySideExtensions() && metaClass->isQObject()) { + if (usePySideExtensions() && isQObject(metaClass)) { s << "Shiboken::ObjectType::setSubTypeInitHook(pyType, &PySide::initQObjectSubType);\n" - << "PySide::initDynamicMetaObject(pyType, &::" - << metaClass->qualifiedCppName() << "::staticMetaObject, sizeof("; - if (shouldGenerateCppWrapper(metaClass)) - s << wrapperName(metaClass); - else - s << "::" << metaClass->qualifiedCppName(); - s << "));\n"; + << "PySide::initDynamicMetaObject(pyType, &" << m_gsp + << metaClass->qualifiedCppName() << "::staticMetaObject, sizeof(" + << (shouldGenerateCppWrapper(metaClass) + ? wrapperName(metaClass) : getFullTypeName(metaClass)) + << "));\n"; } - s << outdent << "}\n"; + s << "\nreturn pyType;\n" << outdent << "}\n"; } -void CppGenerator::writeStaticFieldInitialization(TextStream &s, const AbstractMetaClass *metaClass) +void CppGenerator::writeStaticFieldInitialization(TextStream &s, + const AbstractMetaClassCPtr &metaClass) { - s << "\nvoid " << getSimpleClassStaticFieldsInitFunctionName(metaClass) - << "()\n{\n" << indent << "auto dict = reinterpret_cast<PyTypeObject *>(" - << cpythonTypeName(metaClass) << ")->tp_dict;\n"; + // cpythonTypeName == "Sbk_QRhiShaderResourceBinding_Data_TypeF" + QString name = cpythonTypeName(metaClass); + const auto parts = QStringView{name}.split(u'_', Qt::SkipEmptyParts); + if (parts.size() < 4) { + s << "\nPyTypeObject *" << getSimpleClassStaticFieldsInitFunctionName(metaClass) + << "(PyObject *module)\n{\n" << indent + << "auto *obType = PyObject_GetAttrString(module, \"" << metaClass->name() << "\");\n" + << "auto *type = reinterpret_cast<PyTypeObject *>(obType);\n" + << "Shiboken::AutoDecRef dict(PepType_GetDict(type));\n"; + } else { + s << "\nPyTypeObject *" << getSimpleClassStaticFieldsInitFunctionName(metaClass) + << "(PyObject *module)\n{\n" << indent + << "auto *obContainerType = PyObject_GetAttrString(module, \"" + << parts.at(1) << "\");\n" + << "auto *obType = PyObject_GetAttrString(obContainerType, \"" + << parts.at(2) << "\");\n" + << "auto *type = reinterpret_cast<PyTypeObject *>(obType);\n" + << "Shiboken::AutoDecRef dict(PepType_GetDict(type));\n"; + } for (const AbstractMetaField &field : metaClass->fields()) { if (field.isStatic()) { s << "PyDict_SetItemString(dict, \"" << field.name() @@ -6201,7 +5706,7 @@ void CppGenerator::writeStaticFieldInitialization(TextStream &s, const AbstractM s << ");\n"; } } - s << '\n' << outdent << "}\n"; + s << "return type;\n" << outdent << "}\n"; } enum class QtRegisterMetaType @@ -6209,7 +5714,7 @@ enum class QtRegisterMetaType None, Pointer, Value }; -static bool hasQtMetaTypeRegistrationSpec(const AbstractMetaClass *c) +static bool hasQtMetaTypeRegistrationSpec(const AbstractMetaClassCPtr &c) { return c->typeEntry()->qtMetaTypeRegistration() != TypeSystem::QtMetaTypeRegistration::Unspecified; @@ -6218,7 +5723,7 @@ static bool hasQtMetaTypeRegistrationSpec(const AbstractMetaClass *c) // Returns if and how to register the Qt meta type. By default, "pointer" for // non-QObject object types and "value" for non-abstract, default-constructible // value types. -QtRegisterMetaType qtMetaTypeRegistration(const AbstractMetaClass *c) +QtRegisterMetaType qtMetaTypeRegistration(const AbstractMetaClassCPtr &c) { if (c->isNamespace()) return QtRegisterMetaType::None; @@ -6237,7 +5742,7 @@ QtRegisterMetaType qtMetaTypeRegistration(const AbstractMetaClass *c) // Is there a "base" specification in some base class, meaning only the // base class is to be registered? - if (auto *base = recurseClassHierarchy(c, hasQtMetaTypeRegistrationSpec)) { + if (auto base = recurseClassHierarchy(c, hasQtMetaTypeRegistrationSpec)) { const auto baseSpec = base->typeEntry()->qtMetaTypeRegistration(); if (baseSpec == TypeSystem::QtMetaTypeRegistration::BaseEnabled) return QtRegisterMetaType::None; @@ -6245,7 +5750,7 @@ QtRegisterMetaType qtMetaTypeRegistration(const AbstractMetaClass *c) // Default. if (isObject) - return c->isQObject() ? QtRegisterMetaType::None : QtRegisterMetaType::Pointer; + return isQObject(c) ? QtRegisterMetaType::None : QtRegisterMetaType::Pointer; return !c->isAbstract() && c->isDefaultConstructible() ? QtRegisterMetaType::Value : QtRegisterMetaType::None; @@ -6253,7 +5758,7 @@ QtRegisterMetaType qtMetaTypeRegistration(const AbstractMetaClass *c) void CppGenerator::writeInitQtMetaTypeFunctionBody(TextStream &s, const GeneratorContext &context) { - const AbstractMetaClass *metaClass = context.metaClass(); + const auto metaClass = context.metaClass(); // Gets all class name variants used on different possible scopes QStringList nameVariants; if (!context.forSmartPointer()) @@ -6261,7 +5766,7 @@ void CppGenerator::writeInitQtMetaTypeFunctionBody(TextStream &s, const Generato else nameVariants << context.preciseType().cppSignature(); - const AbstractMetaClass *enclosingClass = metaClass->enclosingClass(); + AbstractMetaClassCPtr enclosingClass = metaClass->enclosingClass(); while (enclosingClass) { if (enclosingClass->typeEntry()->generateCode()) nameVariants << (enclosingClass->name() + u"::"_s + nameVariants.constLast()); @@ -6280,55 +5785,67 @@ void CppGenerator::writeInitQtMetaTypeFunctionBody(TextStream &s, const Generato case QtRegisterMetaType::None: break; case QtRegisterMetaType::Pointer: - s << "qRegisterMetaType< ::" << className << " *>();\n"; + s << "qRegisterMetaType< " << m_gsp << className << " *>();\n"; break; case QtRegisterMetaType::Value: for (const QString &name : std::as_const(nameVariants)) - s << "qRegisterMetaType< ::" << className << " >(\"" << name << "\");\n"; + s << "qRegisterMetaType< " << m_gsp << className << " >(\"" << name << "\");\n"; break; } for (const AbstractMetaEnum &metaEnum : metaClass->enums()) { if (!metaEnum.isPrivate() && !metaEnum.isAnonymous()) { for (const QString &name : std::as_const(nameVariants)) { - s << "qRegisterMetaType< ::" + s << "qRegisterMetaType< " << m_gsp << metaEnum.typeEntry()->qualifiedCppName() << " >(\"" << name << "::" << metaEnum.name() << "\");\n"; } - if (metaEnum.typeEntry()->flags()) { - QString n = metaEnum.typeEntry()->flags()->originalName(); - s << "qRegisterMetaType< ::" << n << " >(\"" << n << "\");\n"; - } } } } -void CppGenerator::writeTypeDiscoveryFunction(TextStream &s, const AbstractMetaClass *metaClass) +void CppGenerator::replacePolymorphicIdPlaceHolders(const AbstractMetaClassCPtr &metaClass, + QString *id) +{ + if (id->contains("%1"_L1)) { + QString replacement = " reinterpret_cast< "_L1 + m_gsp + metaClass->qualifiedCppName() + + " *>(cptr)"_L1; + id->replace("%1"_L1, replacement); + } + if (id->contains("%B"_L1)) { + auto baseClass = metaClass; + while (!baseClass->typeEntry()->isPolymorphicBase() && baseClass->baseClass()) + baseClass = baseClass->baseClass(); + QString replacement = " reinterpret_cast< "_L1 + m_gsp + baseClass->qualifiedCppName() + + " *>(cptr)"_L1; + id->replace("%B"_L1, replacement); + } +} + +void CppGenerator::writeTypeDiscoveryFunction(TextStream &s, + const AbstractMetaClassCPtr &metaClass) { QString polymorphicExpr = metaClass->typeEntry()->polymorphicIdValue(); s << "static void *" << cpythonBaseName(metaClass) << "_typeDiscovery(void *cptr, PyTypeObject *instanceType)\n{\n" << indent - << sbkUnusedVariableCast(u"cptr"_s) - << sbkUnusedVariableCast(u"instanceType"_s); + << sbkUnusedVariableCast("cptr") + << sbkUnusedVariableCast("instanceType"); if (!polymorphicExpr.isEmpty()) { - polymorphicExpr = polymorphicExpr.replace(u"%1"_s, - u" reinterpret_cast< ::"_s - + metaClass->qualifiedCppName() - + u" *>(cptr)"_s); - s << " if (" << polymorphicExpr << ")\n" << indent + replacePolymorphicIdPlaceHolders(metaClass, &polymorphicExpr); + s << "if (" << polymorphicExpr << ")\n" << indent << "return cptr;\n" << outdent; } else if (metaClass->isPolymorphic()) { const auto &ancestors = metaClass->allTypeSystemAncestors(); - for (auto *ancestor : ancestors) { - if (ancestor->baseClass()) + for (const auto &ancestor : ancestors) { + if (ancestor->baseClass() && !ancestor->typeEntry()->isPolymorphicBase()) continue; if (ancestor->isPolymorphic()) { - s << "if (instanceType == Shiboken::SbkType< ::" + s << "if (instanceType == Shiboken::SbkType< " << m_gsp << ancestor->qualifiedCppName() << " >())\n" << indent - << "return dynamic_cast< ::" << metaClass->qualifiedCppName() - << " *>(reinterpret_cast< ::"<< ancestor->qualifiedCppName() + << "return dynamic_cast< " << getFullTypeName(metaClass) + << " *>(reinterpret_cast< "<< getFullTypeName(ancestor) << " *>(cptr));\n" << outdent; } else { qCWarning(lcShiboken).noquote().nospace() @@ -6342,7 +5859,8 @@ void CppGenerator::writeTypeDiscoveryFunction(TextStream &s, const AbstractMetaC s << "return {};\n" << outdent << "}\n\n"; } -void CppGenerator::writeSetattroDefinition(TextStream &s, const AbstractMetaClass *metaClass) const +void CppGenerator::writeSetattroDefinition(TextStream &s, + const AbstractMetaClassCPtr &metaClass) { s << "static int " << ShibokenGenerator::cpythonSetattroFunctionName(metaClass) << "(PyObject *self, PyObject *name, PyObject *value)\n{\n" << indent; @@ -6352,7 +5870,7 @@ void CppGenerator::writeSetattroDefinition(TextStream &s, const AbstractMetaClas } } -inline void CppGenerator::writeSetattroDefaultReturn(TextStream &s) +void CppGenerator::writeSetattroDefaultReturn(TextStream &s) { s << "return PyObject_GenericSetAttr(self, name, value);\n" << outdent << "}\n\n"; @@ -6362,7 +5880,7 @@ void CppGenerator::writeSetattroFunction(TextStream &s, AttroCheck attroCheck, const GeneratorContext &context) const { Q_ASSERT(!context.forSmartPointer()); - const AbstractMetaClass *metaClass = context.metaClass(); + const auto metaClass = context.metaClass(); writeSetattroDefinition(s, metaClass); // PYSIDE-1019: Switch tp_dict before doing tp_setattro. @@ -6372,10 +5890,10 @@ void CppGenerator::writeSetattroFunction(TextStream &s, AttroCheck attroCheck, // PYSIDE-803: Detect duck-punching; clear cache if a method is set. if (attroCheck.testFlag(AttroCheckFlag::SetattroMethodOverride) && context.useWrapper()) { - s << "if (value && PyCallable_Check(value)) {\n" << indent - << "auto plain_inst = " << cpythonWrapperCPtr(metaClass, u"self"_s) << ";\n" - << "auto inst = dynamic_cast<" << context.wrapperName() << " *>(plain_inst);\n" - << "if (inst)\n" << indent + s << "if (value != nullptr && PyCallable_Check(value) != 0) {\n" << indent + << "auto plain_inst = " << cpythonWrapperCPtr(metaClass, PYTHON_SELF_VAR) << ";\n" + << "auto *inst = dynamic_cast<" << context.wrapperName() << " *>(plain_inst);\n" + << "if (inst != nullptr)\n" << indent << "inst->resetPyMethodCache();\n" << outdent << outdent << "}\n"; } @@ -6392,7 +5910,7 @@ void CppGenerator::writeSetattroFunction(TextStream &s, AttroCheck attroCheck, Q_ASSERT(func); s << "{\n" << indent << "auto " << CPP_SELF_VAR << " = " - << cpythonWrapperCPtr(metaClass, u"self"_s) << ";\n"; + << cpythonWrapperCPtr(metaClass, PYTHON_SELF_VAR) << ";\n"; writeClassCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionAny, TypeSystem::TargetLangCode, context); s << outdent << "}\n"; @@ -6401,26 +5919,7 @@ void CppGenerator::writeSetattroFunction(TextStream &s, AttroCheck attroCheck, writeSetattroDefaultReturn(s); } -static const char smartPtrComment[] = - "// Try to find the 'name' attribute, by retrieving the PyObject for " - "the corresponding C++ object held by the smart pointer.\n"; - -void CppGenerator::writeSmartPointerSetattroFunction(TextStream &s, - const GeneratorContext &context) const -{ - Q_ASSERT(context.forSmartPointer()); - writeSetattroDefinition(s, context.metaClass()); - s << smartPtrComment - << "if (auto *rawObj = PyObject_CallMethod(self, " << SMART_POINTER_GETTER - << ", 0)) {\n" << indent - << "if (PyObject_HasAttr(rawObj, name) != 0)\n" << indent - << "return PyObject_GenericSetAttr(rawObj, name, value);\n" << outdent - << "Py_DECREF(rawObj);\n" << outdent - << "}\n"; - writeSetattroDefaultReturn(s); -} - -void CppGenerator::writeGetattroDefinition(TextStream &s, const AbstractMetaClass *metaClass) +void CppGenerator::writeGetattroDefinition(TextStream &s, const AbstractMetaClassCPtr &metaClass) { s << "static PyObject *" << cpythonGetattroFunctionName(metaClass) << "(PyObject *self, PyObject *name)\n{\n" << indent; @@ -6430,10 +5929,10 @@ QString CppGenerator::qObjectGetAttroFunction() const { static QString result; if (result.isEmpty()) { - auto qobjectClass = AbstractMetaClass::findClass(api().classes(), qObjectT()); + auto qobjectClass = AbstractMetaClass::findClass(api().classes(), qObjectT); Q_ASSERT(qobjectClass); - result = u"PySide::getMetaDataFromQObject("_s - + cpythonWrapperCPtr(qobjectClass, u"self"_s) + result = u"PySide::getHiddenDataFromQObject("_s + + cpythonWrapperCPtr(qobjectClass, PYTHON_SELF_VAR) + u", self, name)"_s; } return result; @@ -6443,14 +5942,14 @@ void CppGenerator::writeGetattroFunction(TextStream &s, AttroCheck attroCheck, const GeneratorContext &context) const { Q_ASSERT(!context.forSmartPointer()); - const AbstractMetaClass *metaClass = context.metaClass(); + const auto metaClass = context.metaClass(); writeGetattroDefinition(s, metaClass); // PYSIDE-1019: Switch tp_dict before doing tp_getattro. if (usePySideExtensions()) s << "PySide::Feature::Select(self);\n"; - const QString getattrFunc = usePySideExtensions() && metaClass->isQObject() + const QString getattrFunc = usePySideExtensions() && isQObject(metaClass) ? qObjectGetAttroFunction() : u"PyObject_GenericGetAttr(self, name)"_s; if (attroCheck.testFlag(AttroCheckFlag::GetattroOverloads)) { @@ -6462,11 +5961,14 @@ void CppGenerator::writeGetattroFunction(TextStream &s, AttroCheck attroCheck, << "if (Shiboken::Object::isUserType(self)) {\n" << indent; // PYSIDE-772: Perform optimized name mangling. s << "Shiboken::AutoDecRef tmp(_Pep_PrivateMangle(self, name));\n" - << "if (auto *meth = PyDict_GetItem(Py_TYPE(self)->tp_dict, tmp)) {\n" << indent; + << "Shiboken::AutoDecRef tpDict(PepType_GetDict(Py_TYPE(self)));\n" + << "if (auto *meth = PyDict_GetItem(tpDict.object(), tmp)) {\n" << indent; // PYSIDE-1523: PyFunction_Check is not accepting compiled functions. - s << "if (std::strcmp(Py_TYPE(meth)->tp_name, \"compiled_function\") == 0)\n" << indent - << "return Py_TYPE(meth)->tp_descr_get(meth, self, nullptr);\n" << outdent - << "return PyFunction_Check(meth) ? PyMethod_New(meth, self)\n" + s << "if (std::strcmp(Py_TYPE(meth)->tp_name, \"compiled_function\") == 0) {\n" << indent + << "auto descrGetFunc = " + << pyTypeGetSlot("descrgetfunc", "Py_TYPE(meth)", "Py_tp_descr_get") << ";\n" + << "return descrGetFunc(meth, self, nullptr);\n" << outdent + << "}\nreturn PyFunction_Check(meth) ? PyMethod_New(meth, self)\n" << " : " << getattrFunc << ";\n" << outdent << "}\n" << outdent << "}\n"; @@ -6492,7 +5994,7 @@ void CppGenerator::writeGetattroFunction(TextStream &s, AttroCheck attroCheck, Q_ASSERT(func); s << "{\n" << indent << "auto " << CPP_SELF_VAR << " = " - << cpythonWrapperCPtr(metaClass, u"self"_s) << ";\n"; + << cpythonWrapperCPtr(metaClass, PYTHON_SELF_VAR) << ";\n"; writeClassCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionAny, TypeSystem::TargetLangCode, context); s << outdent << "}\n"; @@ -6501,50 +6003,6 @@ void CppGenerator::writeGetattroFunction(TextStream &s, AttroCheck attroCheck, s << "return " << getattrFunc << ";\n" << outdent << "}\n\n"; } -void CppGenerator::writeSmartPointerGetattroFunction(TextStream &s, - const GeneratorContext &context, - const BoolCastFunctionOptional &boolCast) -{ - Q_ASSERT(context.forSmartPointer()); - const AbstractMetaClass *metaClass = context.metaClass(); - writeGetattroDefinition(s, metaClass); - s << "PyObject *tmp = PyObject_GenericGetAttr(self, name);\n" - << "if (tmp)\n" << indent << "return tmp;\n" << outdent - << "if (PyErr_ExceptionMatches(PyExc_AttributeError) == 0)\n" - << indent << "return nullptr;\n" << outdent - << "PyErr_Clear();\n"; - - if (boolCast.has_value()) { - writeSmartPointerCppSelfDefinition(s, context); - s << "if ("; - writeNbBoolExpression(s, boolCast.value(), true /* invert */); - s << ") {\n" << indent - << R"(PyTypeObject *tp = Py_TYPE(self); -PyErr_Format(PyExc_AttributeError, "Attempt to retrieve '%s' from null object '%s'.", - Shiboken::String::toCString(name), tp->tp_name); -return nullptr; -)" << outdent << "}\n"; - } - - // This generates the code which dispatches access to member functions - // and fields from the smart pointer to its pointee. - s << smartPtrComment - << "if (auto *rawObj = PyObject_CallMethod(self, " - << SMART_POINTER_GETTER << ", 0)) {\n" << indent - << "if (auto *attribute = PyObject_GetAttr(rawObj, name))\n" - << indent << "tmp = attribute;\n" << outdent - << "Py_DECREF(rawObj);\n" << outdent - << "}\n" - << "if (!tmp) {\n" << indent - << R"(PyTypeObject *tp = Py_TYPE(self); -PyErr_Format(PyExc_AttributeError, - "'%.50s' object has no attribute '%.400s'", - tp->tp_name, Shiboken::String::toCString(name)); -)" << outdent - << "}\n" - << "return tmp;\n" << outdent << "}\n\n"; -} - void CppGenerator::writeNbBoolExpression(TextStream &s, const BoolCastFunction &f, bool invert) { @@ -6561,7 +6019,7 @@ void CppGenerator::writeNbBoolExpression(TextStream &s, const BoolCastFunction & void CppGenerator::writeNbBoolFunction(const GeneratorContext &context, const BoolCastFunction &f, - TextStream &s) const + TextStream &s) { s << "static int " << cpythonBaseName(context.metaClass()) << "___nb_bool(PyObject *self)\n" << "{\n" << indent; @@ -6585,28 +6043,53 @@ void CppGenerator::writeNbBoolFunction(const GeneratorContext &context, // function. void CppGenerator::writeInitFunc(TextStream &declStr, TextStream &callStr, const QString &initFunctionName, - const TypeEntryCPtr &enclosingEntry) + const TypeEntryCPtr &enclosingEntry, + const QString &pythonName, bool lazy) { - const bool hasParent = - enclosingEntry && enclosingEntry->type() != TypeEntry::TypeSystemType; - declStr << "void init_" << initFunctionName << "(PyObject *" + const QString functionName = "init_"_L1 + initFunctionName; + const bool hasParent = enclosingEntry && enclosingEntry->type() != TypeEntry::TypeSystemType; + declStr << "PyTypeObject *" << functionName << "(PyObject *" << (hasParent ? "enclosingClass" : "module") << ");\n"; - callStr << "init_" << initFunctionName; - if (hasParent) { - callStr << "(reinterpret_cast<PyTypeObject *>(" - << cpythonTypeNameExt(enclosingEntry) << ")->tp_dict);\n"; + + if (!lazy) { + const QString enclosing = hasParent + ? "reinterpret_cast<PyObject *>("_L1 + cpythonTypeNameExt(enclosingEntry) + u')' + : "module"_L1; + callStr << functionName << '(' << enclosing << ");\n"; + } 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, \"" << parts[0] << "\", " + << functionName << ", \"" << namePathPrefix << '.' << pythonName << "\");\n"; } else { - callStr << "(module);\n"; + callStr << "Shiboken::Module::AddTypeCreationFunction(" + << "module, \"" << pythonName << "\", " + << functionName << ");\n"; } } +static void writeSubModuleHandling(TextStream &s, const QString &moduleName, + const QString &subModuleOf) +{ + s << "{\n" << indent + << "Shiboken::AutoDecRef parentModule(Shiboken::Module::import(\"" + << subModuleOf << "\"));\n" + << "if (parentModule.isNull())\n" << indent + << "return nullptr;\n" << outdent + << "if (PyModule_AddObject(parentModule.object(), \"" << moduleName + << "\", module) < 0)\n" + << indent << "return nullptr;\n" << outdent << outdent << "}\n"; +} + bool CppGenerator::finishGeneration() { //Generate CPython wrapper file StringStream s_classInitDecl(TextStream::Language::Cpp); StringStream s_classPythonDefines(TextStream::Language::Cpp); - QSet<Include> includes; + std::set<Include> includes; StringStream s_globalFunctionImpl(TextStream::Language::Cpp); StringStream s_globalFunctionDef(TextStream::Language::Cpp); StringStream signatureStream(TextStream::Language::Cpp); @@ -6615,8 +6098,8 @@ bool CppGenerator::finishGeneration() for (auto it = functionGroups.cbegin(), end = functionGroups.cend(); it != end; ++it) { const AbstractMetaFunctionCList &overloads = it.value(); for (const auto &func : overloads) { - if (func->typeEntry()) - includes << func->typeEntry()->include(); + if (auto te = func->typeEntry()) + includes.insert(te->include()); } if (overloads.isEmpty()) @@ -6632,50 +6115,61 @@ bool CppGenerator::finishGeneration() } AbstractMetaClassCList classesWithStaticFields; - for (auto cls : api().classes()){ + for (const auto &cls : api().classes()){ auto te = cls->typeEntry(); if (shouldGenerate(te)) { + const bool hasConfigCondition = te->hasConfigCondition(); + if (hasConfigCondition) { + s_classInitDecl << te->configCondition() << '\n'; + s_classPythonDefines << te->configCondition() << '\n'; + } writeInitFunc(s_classInitDecl, s_classPythonDefines, getSimpleClassInitFunctionName(cls), - targetLangEnclosingEntry(te)); + targetLangEnclosingEntry(te), cls->name()); if (cls->hasStaticFields()) { - s_classInitDecl << "void " - << getSimpleClassStaticFieldsInitFunctionName(cls) << "();\n"; + s_classInitDecl << "PyTypeObject *" + << getSimpleClassStaticFieldsInitFunctionName(cls) << "(PyObject *module);\n"; classesWithStaticFields.append(cls); } + if (hasConfigCondition) { + s_classInitDecl << "#endif\n"; + s_classPythonDefines << "#endif\n"; + } } } // Initialize smart pointer types. for (const auto &smp : api().instantiatedSmartPointers()) { GeneratorContext context = contextForSmartPointer(smp.specialized, smp.type); - auto *enclosingClass = context.metaClass()->enclosingClass(); - auto enclosingTypeEntry = enclosingClass != nullptr + const auto enclosingClass = context.metaClass()->enclosingClass(); + auto enclosingTypeEntry = enclosingClass ? enclosingClass->typeEntry() : targetLangEnclosingEntry(smp.type.typeEntry()); + writeInitFunc(s_classInitDecl, s_classPythonDefines, getInitFunctionName(context), - enclosingTypeEntry); - includes << smp.type.instantiations().constFirst().typeEntry()->include(); + enclosingTypeEntry, smp.type.name()); + includes.insert(smp.type.instantiations().constFirst().typeEntry()->include()); } for (auto &instantiatedContainer : api().instantiatedContainers()) { + includes.insert(instantiatedContainer.typeEntry()->include()); for (const auto &inst : instantiatedContainer.instantiations()) - includes << inst.typeEntry()->include(); + includes.insert(inst.typeEntry()->include()); } const ExtendedConverterData extendedConverters = getExtendedConverters(); for (auto it = extendedConverters.cbegin(), end = extendedConverters.cend(); it != end; ++it) { TypeEntryCPtr te = it.key(); - includes << te->include(); + includes.insert(te->include()); for (const auto &metaClass : it.value()) - includes << metaClass->typeEntry()->include(); + includes.insert(metaClass->typeEntry()->include()); } const QList<CustomConversionPtr> &typeConversions = getPrimitiveCustomConversions(); for (const auto &c : typeConversions) { - if (auto te = c->ownerType(); !te.isNull()) - includes << te->include(); + if (auto te = c->ownerType()) + includes.insert(te->include()); } QString moduleFileName(outputDirectory() + u'/' + subDirectoryForPackage(packageName())); @@ -6707,13 +6201,13 @@ bool CppGenerator::finishGeneration() } s << "#include \"" << getModuleHeaderFileName() << '"' << "\n\n"; - for (const Include &include : std::as_const(includes)) + for (const Include &include : includes) s << include; s << '\n'; // Global enums AbstractMetaEnumList globalEnums = api().globalEnums(); - for (const AbstractMetaClass *nsp : invisibleTopNamespaces()) { + for (const auto &nsp : invisibleTopNamespaces()) { const auto oldSize = globalEnums.size(); nsp->getEnumsToBeGenerated(&globalEnums); if (globalEnums.size() > oldSize) @@ -6722,7 +6216,7 @@ bool CppGenerator::finishGeneration() TypeDatabase *typeDb = TypeDatabase::instance(); TypeSystemTypeEntryCPtr moduleEntry = typeDb->defaultTypeSystemType(); - Q_ASSERT(!moduleEntry.isNull()); + Q_ASSERT(moduleEntry); s << '\n'; // Extra includes @@ -6737,29 +6231,34 @@ bool CppGenerator::finishGeneration() s << '\n'; } + // FIXME PYSIDE-7: Remove backwards compatible structure s << "// Current module's type array.\n" - << "PyTypeObject **" << cppApiVariableName() << " = nullptr;\n" + << "Shiboken::Module::TypeInitStruct *" << cppApiVariableName() << " = nullptr;\n" + << "// Backwards compatible structure with identical indexing.\n" + << "PyTypeObject **" << cppApiVariableNameOld() << " = nullptr;\n" << "// Current module's PyObject pointer.\n" << "PyObject *" << pythonModuleObjectName() << " = nullptr;\n" << "// Current module's converter array.\n" - << "SbkConverter **" << convertersVariableName() << " = nullptr;\n"; + << "SbkConverter **" << convertersVariableName() << " = nullptr;\n\n"; const CodeSnipList snips = moduleEntry->codeSnips(); // module inject-code native/beginning - if (!snips.isEmpty()) - writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::NativeCode); + writeModuleCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::NativeCode); // cleanup staticMetaObject attribute if (usePySideExtensions()) { + QString iType = cppApiVariableName() + "[i].type"_L1; + QString iName = cppApiVariableName() + "[i].fullName"_L1; + s << "void cleanTypesAttributes() {\n" << indent << "static PyObject *attrName = Shiboken::PyName::qtStaticMetaObject();\n" - << "for (int i = 0, imax = SBK_" << moduleName() - << "_IDX_COUNT; i < imax; i++) {\n" << indent - << "PyObject *pyType = reinterpret_cast<PyObject *>(" << cppApiVariableName() << "[i]);\n" - << "if (pyType && PyObject_HasAttr(pyType, attrName))\n" << indent + << "const int imax = SBK_" << moduleName() << "_IDX_COUNT;\n" + << "for (int i = 0; i < imax && " << iName << " != nullptr; ++i) {\n" << indent + << "auto *pyType = reinterpret_cast<PyObject *>(" << iType << ");\n" + << "if (pyType != nullptr && PyObject_HasAttr(pyType, attrName))\n" << indent << "PyObject_SetAttr(pyType, attrName, Py_None);\n" << outdent - << outdent << "}\n" << outdent << "}\n"; + << outdent << "}\n" << outdent << "}\n\n"; } s << "// Global functions " @@ -6767,7 +6266,7 @@ bool CppGenerator::finishGeneration() << s_globalFunctionImpl.toString() << '\n' << "static PyMethodDef " << moduleName() << "_methods[] = {\n" << indent << s_globalFunctionDef.toString() - << methodDefSentinel << outdent << "};\n\n" + << METHOD_DEF_SENTINEL << outdent << "};\n\n" << "// Classes initialization functions " << "------------------------------------------------------------\n" << s_classInitDecl.toString() << '\n'; @@ -6788,7 +6287,6 @@ bool CppGenerator::finishGeneration() << "} // namespace Shiboken\n\n"; } - writeFlagsNumberMethodsDefinitions(s, globalEnums); s << '\n'; } @@ -6796,7 +6294,7 @@ bool CppGenerator::finishGeneration() if (!requiredModules.isEmpty()) s << "// Required modules' type and converter arrays.\n"; for (const QString &requiredModule : requiredModules) { - s << "PyTypeObject **" << cppApiVariableName(requiredModule) << ";\n" + s << "Shiboken::Module::TypeInitStruct *" << cppApiVariableName(requiredModule) << ";\n" << "SbkConverter **" << convertersVariableName(requiredModule) << ";\n"; } @@ -6808,7 +6306,7 @@ bool CppGenerator::finishGeneration() TypeEntryCPtr externalType = it.key(); s << "// Extended implicit conversions for " << externalType->qualifiedTargetLangName() << '.' << '\n'; - for (const AbstractMetaClass *sourceClass : it.value()) { + for (const auto &sourceClass : it.value()) { AbstractMetaType sourceType = AbstractMetaType::fromAbstractMetaClass(sourceClass); AbstractMetaType targetType = AbstractMetaType::fromTypeEntry(externalType); writePythonToCppConversionFunctions(s, sourceType, targetType); @@ -6828,14 +6326,17 @@ bool CppGenerator::finishGeneration() QHash<AbstractMetaType, OpaqueContainerData> opaqueContainers; const auto &containers = api().instantiatedContainers(); + QSet<AbstractMetaType> valueConverters; if (!containers.isEmpty()) { s << "// Container Type converters.\n\n"; for (const AbstractMetaType &container : containers) { - s << "// C++ to Python conversion for container type '" << container.cppSignature() << "'.\n"; + s << "// C++ to Python conversion for container type '" + << container.cppSignature() << "'.\n"; writeContainerConverterFunctions(s, container); if (container.generateOpaqueContainer()) { - opaqueContainers.insert(container, - writeOpaqueContainerConverterFunctions(s, container)); + auto data = writeOpaqueContainerConverterFunctions(s, container, + &valueConverters); + opaqueContainers.insert(container, data); } } s << '\n'; @@ -6867,6 +6368,8 @@ bool CppGenerator::finishGeneration() // PYSIDE-510: Create a signatures string for the introspection feature. writeSignatureStrings(s, signatureStream.toString(), moduleName(), "global functions"); + writeInitInheritance(s); + // Write module init function const QString globalModuleVar = pythonModuleObjectName(); s << "extern \"C\" LIBSHIBOKEN_EXPORT PyObject *PyInit_" @@ -6876,8 +6379,8 @@ bool CppGenerator::finishGeneration() << indent << "return " << globalModuleVar << ";\n" << outdent; // module inject-code target/beginning - if (!snips.isEmpty()) - writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::TargetLangCode); + writeModuleCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, + TypeSystem::TargetLangCode); for (const QString &requiredModule : requiredModules) { s << "{\n" << indent @@ -6893,9 +6396,25 @@ bool CppGenerator::finishGeneration() int maxTypeIndex = getMaxTypeIndex() + api().instantiatedSmartPointers().size(); if (maxTypeIndex) { - s << "// Create an array of wrapper types for the current module.\n" - << "static PyTypeObject *cppApi[SBK_" << moduleName() << "_IDX_COUNT];\n" - << cppApiVariableName() << " = cppApi;\n\n"; + s << "// Create an array of wrapper types/names for the current module.\n" + << "static Shiboken::Module::TypeInitStruct cppApi[] = {\n" << indent; + + // Windows did not like an array of QString. + QStringList typeNames; + for (int idx = 0; idx < maxTypeIndex; ++idx) + typeNames.append("+++ unknown entry #"_L1 + QString::number(idx) + + " in "_L1 + moduleName()); + + collectFullTypeNamesArray(typeNames); + + for (auto typeName : typeNames) + s << "{nullptr, \"" << typeName << "\"},\n"; + + 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"; } s << "// Create an array of primitive type converters for the current module.\n" @@ -6905,8 +6424,13 @@ bool CppGenerator::finishGeneration() << "PyObject *module = Shiboken::Module::create(\"" << moduleName() << "\", &moduledef);\n\n" << "// Make module available from global scope\n" - << globalModuleVar << " = module;\n\n" - << "// Initialize classes in the type system\n" + << globalModuleVar << " = module;\n\n"; + + const QString subModuleOf = typeDb->defaultTypeSystemType()->subModuleOf(); + if (!subModuleOf.isEmpty()) + writeSubModuleHandling(s, moduleName(), subModuleOf); + + s << "// Initialize classes in the type system\n" << s_classPythonDefines.toString(); if (!typeConversions.isEmpty()) { @@ -6920,7 +6444,7 @@ bool CppGenerator::finishGeneration() if (!containers.isEmpty()) { s << '\n'; for (const AbstractMetaType &container : containers) { - const QString converterObj = writeContainerConverterInitialization(s, container); + const QString converterObj = writeContainerConverterInitialization(s, container, api()); const auto it = opaqueContainers.constFind(container); if (it != opaqueContainers.constEnd()) { writeSetPythonToCppPointerConversion(s, converterObj, @@ -6955,7 +6479,7 @@ bool CppGenerator::finishGeneration() } } - writeEnumsInitialization(s, globalEnums, ErrorReturn::Default); + writeEnumsInitialization(s, globalEnums); s << "// Register primitive types converters.\n"; const PrimitiveTypeEntryCList &primitiveTypeList = primitiveTypes(); @@ -6965,14 +6489,7 @@ bool CppGenerator::finishGeneration() if (!pte->referencesType()) continue; TypeEntryCPtr referencedType = basicReferencedTypeEntry(pte); - QString converter = converterObject(referencedType); - QStringList cppSignature = pte->qualifiedCppName().split(u"::"_s, Qt::SkipEmptyParts); - while (!cppSignature.isEmpty()) { - QString signature = cppSignature.join(u"::"_s); - s << "Shiboken::Conversions::registerConverterName(" - << converter << ", \"" << signature << "\");\n"; - cppSignature.removeFirst(); - } + registerConverterInScopes(s, pte->qualifiedCppName(), converterObject(referencedType)); } s << '\n'; @@ -6984,27 +6501,29 @@ bool CppGenerator::finishGeneration() // of the previously registered types (PYSIDE-1529). if (!classesWithStaticFields.isEmpty()) { s << "\n// Static field initialization\n"; - for (auto cls : std::as_const(classesWithStaticFields)) - s << getSimpleClassStaticFieldsInitFunctionName(cls) << "();\n"; + for (const auto &cls : std::as_const(classesWithStaticFields)) { + ConfigurableScope configScope(s, cls->typeEntry()); + s << getSimpleClassStaticFieldsInitFunctionName(cls) << "(module);\n"; + } } - s << "\nif (PyErr_Occurred()) {\n" << indent + s << '\n' << initInheritanceFunction << "();\n" + << "\nif (" << shibokenErrorsOccurred << ") {\n" << indent << "PyErr_Print();\n" << "Py_FatalError(\"can't initialize module " << moduleName() << "\");\n" << outdent << "}\n"; // module inject-code target/end - if (!snips.isEmpty()) - writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::TargetLangCode); + writeModuleCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::TargetLangCode); // module inject-code native/end - if (!snips.isEmpty()) - writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode); + writeModuleCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode); if (usePySideExtensions()) { for (const AbstractMetaEnum &metaEnum : std::as_const(globalEnums)) if (!metaEnum.isAnonymous()) { - s << "qRegisterMetaType< ::" << metaEnum.typeEntry()->qualifiedCppName() + ConfigurableScope configScope(s, metaEnum.typeEntry()); + s << "qRegisterMetaType< " << getFullTypeName(metaEnum.typeEntry()) << " >(\"" << metaEnum.name() << "\");\n"; } @@ -7012,7 +6531,7 @@ bool CppGenerator::finishGeneration() s << "PySide::registerCleanupFunction(cleanTypesAttributes);\n\n"; } - // finish the rest of __signature__ initialization. + // finish the rest of get_signature() initialization. s << "FinishSignatureInitialization(module, " << moduleName() << "_SignatureStrings);\n" << "\nreturn module;\n" << outdent << "}\n"; @@ -7038,17 +6557,17 @@ static bool useParentHeuristics(const ApiExtractorResult &api, { if (!ComplexTypeEntry::isParentManagementEnabled()) // FIXME PYSIDE 7: Remove this return true; - auto *owner = func->ownerClass(); - if (owner == nullptr) + const auto owner = func->ownerClass(); + if (!owner) return false; - auto ownerEntry = owner->parentManagementEntry(); - if (ownerEntry.isNull()) + auto ownerEntry = parentManagementEntry(owner); + if (!ownerEntry) return false; auto argTypeEntry = argType.typeEntry(); if (!argTypeEntry->isComplex()) return false; - auto *argClass = AbstractMetaClass::findClass(api.classes(), argTypeEntry); - return argClass != nullptr && argClass->parentManagementEntry() == ownerEntry; + const auto argClass = AbstractMetaClass::findClass(api.classes(), argTypeEntry); + return argClass && parentManagementEntry(argClass) == ownerEntry; } bool CppGenerator::writeParentChildManagement(TextStream &s, const AbstractMetaFunctionCPtr &func, @@ -7087,7 +6606,7 @@ bool CppGenerator::writeParentChildManagement(TextStream &s, const AbstractMetaF if (parentIndex == 0) { parentVariable = PYTHON_RETURN_VAR; } else if (parentIndex == -1) { - parentVariable = u"self"_s; + parentVariable = PYTHON_SELF_VAR; } else { parentVariable = usePyArgs ? pythonArgsAt(parentIndex - 1) : PYTHON_ARG; @@ -7097,7 +6616,7 @@ bool CppGenerator::writeParentChildManagement(TextStream &s, const AbstractMetaF if (childIndex == 0) { childVariable = PYTHON_RETURN_VAR; } else if (childIndex == -1) { - childVariable = u"self"_s; + childVariable = PYTHON_SELF_VAR; } else { childVariable = usePyArgs ? pythonArgsAt(childIndex - 1) : PYTHON_ARG; @@ -7154,9 +6673,9 @@ void CppGenerator::writeReturnValueHeuristics(TextStream &s, const AbstractMetaF } } -void CppGenerator::writeHashFunction(TextStream &s, const GeneratorContext &context) const +void CppGenerator::writeHashFunction(TextStream &s, const GeneratorContext &context) { - const AbstractMetaClass *metaClass = context.metaClass(); + const auto metaClass = context.metaClass(); const char hashType[] = "Py_hash_t"; s << "static " << hashType << ' ' << cpythonBaseName(metaClass) << "_HashFunc(PyObject *self)\n{\n" << indent; @@ -7180,7 +6699,7 @@ void CppGenerator::writeHashFunction(TextStream &s, const GeneratorContext &cont void CppGenerator::writeDefaultSequenceMethods(TextStream &s, const GeneratorContext &context) const { - const AbstractMetaClass *metaClass = context.metaClass(); + const auto metaClass = context.metaClass(); ErrorReturn errorReturn = ErrorReturn::Zero; // __len__ @@ -7247,14 +6766,20 @@ void CppGenerator::writeIndexError(TextStream &s, const QString &errorMsg, << errorReturn << outdent << "}\n"; } +QString CppGenerator::writeReprFunctionHeader(TextStream &s, const GeneratorContext &context) +{ + QString funcName = cpythonBaseName(context.metaClass()) + REPR_FUNCTION; + s << "extern \"C\"\n{\n" + << "static PyObject *" << funcName << "(PyObject *self)\n{\n" << indent; + return funcName; +} + QString CppGenerator::writeReprFunction(TextStream &s, const GeneratorContext &context, - uint indirections) const + uint indirections) { - const AbstractMetaClass *metaClass = context.metaClass(); - QString funcName = cpythonBaseName(metaClass) + reprFunction(); - s << "extern \"C\"\n{\n" - << "static PyObject *" << funcName << "(PyObject *self)\n{\n" << indent; + const auto metaClass = context.metaClass(); + QString funcName = writeReprFunctionHeader(s, context); writeCppSelfDefinition(s, context); s << R"(QBuffer buffer; buffer.open(QBuffer::ReadWrite); @@ -7269,15 +6794,21 @@ const auto idx = str.indexOf('('); auto *typeName = Py_TYPE(self)->tp_name; if (idx >= 0) )" << indent << "str.replace(0, idx, typeName);\n" << outdent - << "str = str.trimmed();\n" - << "PyObject *mod = PyDict_GetItem(Py_TYPE(self)->tp_dict, Shiboken::PyMagicName::module());\n"; + << "str = str.trimmed();\n" + << "Shiboken::AutoDecRef tpDict(PepType_GetDict(Py_TYPE(self)));\n" + << "PyObject *mod = PyDict_GetItem(tpDict.object(), Shiboken::PyMagicName::module());\n"; // PYSIDE-595: The introduction of heap types has the side effect that the module name // is always prepended to the type name. Therefore the strchr check: s << "if (mod != nullptr && std::strchr(typeName, '.') == nullptr)\n" << indent << "return Shiboken::String::fromFormat(\"<%s.%s at %p>\"," " Shiboken::String::toCString(mod), str.constData(), self);\n" << outdent - << "return Shiboken::String::fromFormat(\"<%s at %p>\", str.constData(), self);\n" - << outdent << "}\n} // extern C\n\n"; + << "return Shiboken::String::fromFormat(\"<%s at %p>\", str.constData(), self);\n"; + writeReprFunctionFooter(s); return funcName; } + +void CppGenerator::writeReprFunctionFooter(TextStream &s) +{ + s << outdent << "}\n} // extern C\n\n"; +} |