diff options
Diffstat (limited to 'sources/shiboken2/generator/shiboken2/cppgenerator.cpp')
-rw-r--r-- | sources/shiboken2/generator/shiboken2/cppgenerator.cpp | 3400 |
1 files changed, 1927 insertions, 1473 deletions
diff --git a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp index 5460fd7c7..786308023 100644 --- a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp @@ -29,10 +29,12 @@ #include <memory> #include "cppgenerator.h" +#include "ctypenames.h" #include "fileout.h" #include "overloaddata.h" #include <abstractmetalang.h> #include <messages.h> +#include <propertyspec.h> #include <reporthandler.h> #include <typedatabase.h> @@ -44,8 +46,7 @@ #include <QMetaType> #include <algorithm> - -#include <algorithm> +#include <cstring> static const char CPP_ARG0[] = "cppArg0"; @@ -84,10 +85,9 @@ static const char *typeNameOf(const T &t) inline AbstractMetaType *getTypeWithoutContainer(AbstractMetaType *arg) { if (arg && arg->typeEntry()->isContainer()) { - AbstractMetaTypeList lst = arg->instantiations(); // only support containers with 1 type - if (lst.size() == 1) - return lst[0]; + if (arg->instantiations().size() == 1) + return arg->instantiations().constFirst(); } return arg; } @@ -151,16 +151,16 @@ CppGenerator::CppGenerator() QLatin1String("PyObject*")}); m_sequenceProtocol.insert(QLatin1String("__setitem__"), {QLatin1String("PyObject *self, Py_ssize_t _i, PyObject *_value"), - QLatin1String("int")}); + intT()}); m_sequenceProtocol.insert(QLatin1String("__getslice__"), {QLatin1String("PyObject *self, Py_ssize_t _i1, Py_ssize_t _i2"), QLatin1String("PyObject*")}); m_sequenceProtocol.insert(QLatin1String("__setslice__"), {QLatin1String("PyObject *self, Py_ssize_t _i1, Py_ssize_t _i2, PyObject *_value"), - QLatin1String("int")}); + intT()}); m_sequenceProtocol.insert(QLatin1String("__contains__"), {QLatin1String("PyObject *self, PyObject *_value"), - QLatin1String("int")}); + intT()}); m_sequenceProtocol.insert(QLatin1String("__concat__"), {QLatin1String("PyObject *self, PyObject *_other"), QLatin1String("PyObject*")}); @@ -183,7 +183,7 @@ CppGenerator::CppGenerator() QLatin1String("PyObject*")}); m_mappingProtocol.insert(QLatin1String("__msetitem__"), {QLatin1String("PyObject *self, PyObject *_key, PyObject *_value"), - QLatin1String("int")}); + intT()}); // Sequence protocol structure members names m_mpFuncs.insert(QLatin1String("__mlen__"), QLatin1String("mp_length")); @@ -196,7 +196,7 @@ QString CppGenerator::fileNameSuffix() const return QLatin1String("_wrapper.cpp"); } -QString CppGenerator::fileNameForContext(GeneratorContext &context) const +QString CppGenerator::fileNameForContext(const GeneratorContext &context) const { const AbstractMetaClass *metaClass = context.metaClass(); if (!context.forSmartPointer()) { @@ -246,7 +246,7 @@ const AbstractMetaFunction *CppGenerator::boolCast(const AbstractMetaClass *meta return nullptr; // TODO: This could be configurable someday const AbstractMetaFunction *func = metaClass->findFunction(QLatin1String("isNull")); - if (!func || !func->type() || !func->type()->typeEntry()->isPrimitive() || !func->isPublic()) + if (!func || func->isVoid() || !func->type()->typeEntry()->isPrimitive() || !func->isPublic()) return nullptr; auto pte = static_cast<const PrimitiveTypeEntry *>(func->type()->typeEntry()); while (pte->referencedTypeEntry()) @@ -255,6 +255,15 @@ const AbstractMetaFunction *CppGenerator::boolCast(const AbstractMetaClass *meta && func->arguments().isEmpty() ? func : nullptr; } +const AbstractMetaType *CppGenerator::findSmartPointerInstantiation(const TypeEntry *entry) const +{ + for (auto i : instantiatedSmartPointers()) { + if (i->instantiations().at(0)->typeEntry() == entry) + return i; + } + return nullptr; +} + using FunctionGroupMap = QMap<QString, AbstractMetaFunctionList>; // Prevent ELF symbol qt_version_tag from being generated into the source @@ -288,44 +297,75 @@ static inline bool canGenerateFieldSetter(const AbstractMetaField *field) return !type->isConstant() || isPointerToConst(type); } +static bool isStdSetterName(QString setterName, QString propertyName) +{ + return setterName.size() == propertyName.size() + 3 + && setterName.startsWith(QLatin1String("set")) + && setterName.endsWith(propertyName.rightRef(propertyName.size() - 1)) + && setterName.at(3) == propertyName.at(0).toUpper(); +} + +static QString buildPropertyString(QPropertySpec *spec) +{ + QString text; + text += QLatin1Char('"'); + text += spec->name(); + text += QLatin1Char(':'); + + if (spec->read() != spec->name()) + text += spec->read(); + + if (!spec->write().isEmpty()) { + text += QLatin1Char(':'); + if (!isStdSetterName(spec->write(), spec->name())) + text += spec->write(); + } + + text += QLatin1Char('"'); + return text; +} + +static void writePyGetSetDefEntry(QTextStream &s, const QString &name, + const QString &getFunc, const QString &setFunc) +{ + s << "{const_cast<char *>(\"" << name << "\"), " << getFunc << ", " + << (setFunc.isEmpty() ? QLatin1String(NULL_PTR) : setFunc) << "},\n"; +} + /*! Function used to write the class generated binding code on the buffer \param s the output buffer \param metaClass the pointer to metaclass information */ -void CppGenerator::generateClass(QTextStream &s, GeneratorContext &classContext) +void CppGenerator::generateClass(QTextStream &s, const GeneratorContext &classContext) { - AbstractMetaClass *metaClass = classContext.metaClass(); - if (ReportHandler::isDebug(ReportHandler::SparseDebug)) - qCDebug(lcShiboken) << "Generating wrapper implementation for " << metaClass->fullName(); + const AbstractMetaClass *metaClass = classContext.metaClass(); // write license comment - s << licenseComment() << endl; + s << licenseComment() << Qt::endl; if (!avoidProtectedHack() && !metaClass->isNamespace() && !metaClass->hasPrivateDestructor()) { - s << "//workaround to access protected functions" << endl; - s << "#define protected public" << endl << endl; + s << "//workaround to access protected functions\n"; + s << "#define protected public\n\n"; } // headers - s << "// default includes" << endl; - s << "#include <shiboken.h>" << endl; + s << "// default includes\n"; + s << "#include <shiboken.h>\n"; if (usePySideExtensions()) { s << includeQDebug; - s << "#include <pysidesignal.h>" << endl; - s << "#include <pysideproperty.h>" << endl; - s << "#include <pyside.h>" << endl; - s << "#include <destroylistener.h>" << endl; - s << "#include <qapp_macro.h>" << endl; - s << endl; - s << "QT_WARNING_DISABLE_DEPRECATED" << endl; - s << endl; + s << "#include <pysidesignal.h>\n" + << "#include <pysideproperty.h>\n" + << "#include <pyside.h>\n" + << "#include <pysideqenum.h>\n" + << "#include <feature_select.h>\n" + << "QT_WARNING_DISABLE_DEPRECATED\n\n"; } - s << "#include <typeinfo>" << endl; + s << "#include <typeinfo>\n"; if (usePySideExtensions() && metaClass->isQObject()) { - s << "#include <signalmanager.h>" << endl; - s << "#include <pysidemetafunction.h>" << endl; + s << "#include <signalmanager.h>\n"; + s << "#include <pysidemetafunction.h>\n"; } // The multiple inheritance initialization function @@ -333,57 +373,65 @@ void CppGenerator::generateClass(QTextStream &s, GeneratorContext &classContext) if (getMultipleInheritingClass(metaClass) != nullptr) s << "#include <algorithm>\n#include <set>\n"; if (metaClass->generateExceptionHandling()) - s << "#include <exception>" << endl; + s << "#include <exception>\n"; + s << "#include <iterator>\n"; // For containers + + if (wrapperDiagnostics()) + s << "#include <helper.h>\n#include <iostream>\n"; - s << endl << "// module include" << endl << "#include \"" << getModuleHeaderFileName() << '"' << endl; + s << "\n// module include\n" << "#include \"" << getModuleHeaderFileName() << "\"\n"; QString headerfile = fileNameForContext(classContext); headerfile.replace(QLatin1String(".cpp"), QLatin1String(".h")); - s << endl << "// main header" << endl << "#include \"" << headerfile << '"' << endl; + s << "\n// main header\n" << "#include \"" << headerfile << "\"\n"; - s << endl << "// inner classes" << endl; + s << Qt::endl << "// inner classes\n"; const AbstractMetaClassList &innerClasses = metaClass->innerClasses(); for (AbstractMetaClass *innerClass : innerClasses) { - GeneratorContext innerClassContext(innerClass); - if (shouldGenerate(innerClass)) { + GeneratorContext innerClassContext = contextForClass(innerClass); + if (shouldGenerate(innerClass) && !innerClass->typeEntry()->isSmartPointer()) { QString headerfile = fileNameForContext(innerClassContext); headerfile.replace(QLatin1String(".cpp"), QLatin1String(".h")); - s << "#include \"" << headerfile << '"' << endl; + s << "#include \"" << headerfile << "\"\n"; } } AbstractMetaEnumList classEnums = metaClass->enums(); - for (AbstractMetaClass *innerClass : innerClasses) - lookForEnumsInClassesNotToBeGenerated(classEnums, innerClass); + metaClass->getEnumsFromInvisibleNamespacesToBeGenerated(&classEnums); //Extra includes - s << endl << "// Extra includes" << endl; - QVector<Include> includes = metaClass->typeEntry()->extraIncludes(); + QVector<Include> includes; + if (!classContext.useWrapper()) + includes += metaClass->typeEntry()->extraIncludes(); for (AbstractMetaEnum *cppEnum : qAsConst(classEnums)) includes.append(cppEnum->typeEntry()->extraIncludes()); - std::sort(includes.begin(), includes.end()); - for (const Include &inc : qAsConst(includes)) - s << inc.toString() << endl; - s << endl; + if (!includes.isEmpty()) { + s << "\n// Extra includes\n"; + std::sort(includes.begin(), includes.end()); + for (const Include &inc : qAsConst(includes)) + s << inc.toString() << Qt::endl; + s << '\n'; + } s << "\n#include <cctype>\n#include <cstring>\n"; if (metaClass->typeEntry()->typeFlags() & ComplexTypeEntry::Deprecated) - s << "#Deprecated" << endl; + s << "#Deprecated\n"; // Use class base namespace { const AbstractMetaClass *context = metaClass->enclosingClass(); while (context) { - if (context->isNamespace() && !context->enclosingClass()) { - s << "using namespace " << context->qualifiedCppName() << ";" << endl; + if (context->isNamespace() && !context->enclosingClass() + && static_cast<const NamespaceTypeEntry *>(context->typeEntry())->generateUsing()) { + s << "\nusing namespace " << context->qualifiedCppName() << ";\n"; break; } context = context->enclosingClass(); } } - s << endl << endl << typeNameFunc << endl; + s << Qt::endl << Qt::endl << typeNameFunc << Qt::endl; // Create string literal for smart pointer getter method. if (classContext.forSmartPointer()) { @@ -396,46 +444,46 @@ void CppGenerator::generateClass(QTextStream &s, GeneratorContext &classContext) // class inject-code native/beginning if (!metaClass->typeEntry()->codeSnips().isEmpty()) { - writeCodeSnips(s, metaClass->typeEntry()->codeSnips(), TypeSystem::CodeSnipPositionBeginning, TypeSystem::NativeCode, metaClass); - s << endl; + writeClassCodeSnips(s, metaClass->typeEntry()->codeSnips(), + TypeSystem::CodeSnipPositionBeginning, TypeSystem::NativeCode, + classContext); + s << Qt::endl; } // python conversion rules if (metaClass->typeEntry()->hasTargetConversionRule()) { - s << "// Python Conversion" << endl; - s << metaClass->typeEntry()->conversionRule() << endl; + s << "// Python Conversion\n"; + s << metaClass->typeEntry()->conversionRule() << Qt::endl; } - if (shouldGenerateCppWrapper(metaClass)) { - s << "// Native ---------------------------------------------------------" << endl; - s << endl; + if (classContext.useWrapper()) { + s << "// Native ---------------------------------------------------------\n\n"; if (avoidProtectedHack() && usePySideExtensions()) { - s << "void " << wrapperName(metaClass) << "::pysideInitQtMetaTypes()\n{\n"; + s << "void " << classContext.wrapperName() << "::pysideInitQtMetaTypes()\n{\n"; Indentation indent(INDENT); writeInitQtMetaTypeFunctionBody(s, classContext); s << "}\n\n"; } const AbstractMetaFunctionList &funcs = filterFunctions(metaClass); + int maxOverrides = 0; + writeCacheResetNative(s, classContext); for (const AbstractMetaFunction *func : funcs) { const bool notAbstract = !func->isAbstract(); if ((func->isPrivate() && notAbstract && !visibilityModifiedToPrivate(func)) || (func->isModifiedRemoved() && notAbstract)) continue; - if (func->functionType() == AbstractMetaFunction::ConstructorFunction && !func->isUserAdded()) { - writeConstructorNative(s, func); - } else if ((!avoidProtectedHack() || !metaClass->hasPrivateDestructor()) - && ((func->isVirtual() || func->isAbstract()) - && (func->attributes() & AbstractMetaAttributes::FinalCppMethod) == 0)) { - writeVirtualMethodNative(s, func); - } + if (func->functionType() == AbstractMetaFunction::ConstructorFunction && !func->isUserAdded()) + writeConstructorNative(s, classContext, func); + else if (shouldWriteVirtualMethodNative(func)) + writeVirtualMethodNative(s, func, maxOverrides++); } if (!avoidProtectedHack() || !metaClass->hasPrivateDestructor()) { if (usePySideExtensions() && metaClass->isQObject()) - writeMetaObjectMethod(s, metaClass); - writeDestructorNative(s, metaClass); + writeMetaObjectMethod(s, classContext); + writeDestructorNative(s, classContext); } } @@ -448,8 +496,8 @@ void CppGenerator::generateClass(QTextStream &s, GeneratorContext &classContext) QString signaturesString; QTextStream signatureStream(&signaturesString); - s << endl << "// Target ---------------------------------------------------------" << endl << endl; - s << "extern \"C\" {" << endl; + s << "\n// Target ---------------------------------------------------------\n\n" + << "extern \"C\" {\n"; const auto &functionGroups = getFunctionGroups(metaClass); for (auto it = functionGroups.cbegin(), end = functionGroups.cend(); it != end; ++it) { AbstractMetaFunctionList overloads; @@ -536,10 +584,10 @@ void CppGenerator::generateClass(QTextStream &s, GeneratorContext &classContext) writeSignatureInfo(signatureStream, overloads); if (OverloadData::hasStaticAndInstanceFunctions(overloads)) { QString methDefName = cpythonMethodDefinitionName(rfunc); - smd << "static PyMethodDef " << methDefName << " = {" << endl; + smd << "static PyMethodDef " << methDefName << " = {\n"; smd << INDENT; writeMethodDefinitionEntry(smd, overloads); - smd << endl << "};" << endl << endl; + smd << "\n};\n\n"; } writeMethodDefinition(md, overloads); } @@ -549,54 +597,67 @@ void CppGenerator::generateClass(QTextStream &s, GeneratorContext &classContext) if (metaClass->typeEntry()->isValue() || metaClass->typeEntry()->isSmartPointer()) { writeCopyFunction(s, classContext); - signatureStream << fullPythonClassName(metaClass) << ".__copy__()" << endl; + signatureStream << fullPythonClassName(metaClass) << ".__copy__()\n"; } // Write single method definitions s << singleMethodDefinitions; + if (usePySideExtensions()) { + // PYSIDE-1019: Write a compressed list of all properties `name:getter[:setter]`. + // Default values are suppressed. + QStringList sorter; + for (const auto spec : metaClass->propertySpecs()) { + if (!spec->generateGetSetDef()) + sorter.append(buildPropertyString(spec)); + } + sorter.sort(); + + s << '\n'; + s << "static const char *" << className << "_PropertyStrings[] = {\n"; + for (const auto &entry : qAsConst(sorter)) + s << INDENT << entry << ",\n"; + s << INDENT << NULL_PTR << " // Sentinel\n"; + s << "};\n\n"; + } + // Write methods definition - s << "static PyMethodDef " << className << "_methods[] = {" << endl; - s << methodsDefinitions << endl; + s << "static PyMethodDef " << className << "_methods[] = {\n"; + s << methodsDefinitions << Qt::endl; if (metaClass->typeEntry()->isValue() || metaClass->typeEntry()->isSmartPointer()) { s << INDENT << "{\"__copy__\", reinterpret_cast<PyCFunction>(" << className << "___copy__)" - << ", METH_NOARGS}," << endl; + << ", METH_NOARGS},\n"; } - s << INDENT << '{' << NULL_PTR << ", " << NULL_PTR << "} // Sentinel" << endl; - s << "};" << endl << endl; + s << INDENT << '{' << NULL_PTR << ", " << NULL_PTR << "} // Sentinel\n"; + s << "};\n\n"; - // Write tp_getattro function - if ((usePySideExtensions() && metaClass->qualifiedCppName() == QLatin1String("QObject"))) { - writeGetattroFunction(s, classContext); - s << endl; - writeSetattroFunction(s, classContext); - s << endl; + // Write tp_s/getattro function + const AttroCheck attroCheck = checkAttroFunctionNeeds(metaClass); + if (attroCheck.testFlag(AttroCheckFlag::GetattroSmartPointer)) { + writeSmartPointerGetattroFunction(s, classContext); + writeSmartPointerSetattroFunction(s, classContext); } else { - if (classNeedsGetattroFunction(metaClass)) { - writeGetattroFunction(s, classContext); - s << endl; - } - if (classNeedsSetattroFunction(metaClass)) { - writeSetattroFunction(s, classContext); - s << endl; - } + if ((attroCheck & AttroCheckFlag::GetattroMask) != 0) + writeGetattroFunction(s, attroCheck, classContext); + if ((attroCheck & AttroCheckFlag::SetattroMask) != 0) + writeSetattroFunction(s, attroCheck, classContext); } if (const AbstractMetaFunction *f = boolCast(metaClass)) { ErrorCode errorCode(-1); - s << "static int " << cpythonBaseName(metaClass) << "___nb_bool(PyObject *self)" << endl; - s << '{' << endl; + s << "static int " << cpythonBaseName(metaClass) << "___nb_bool(PyObject *self)\n" + << "{\n"; writeCppSelfDefinition(s, classContext); if (f->allowThread()) { - s << INDENT << "int result;" << endl; - s << INDENT << BEGIN_ALLOW_THREADS << endl; - s << INDENT << "result = !" << CPP_SELF_VAR << "->isNull();" << endl; - s << INDENT << END_ALLOW_THREADS << endl; - s << INDENT << "return result;" << endl; + s << INDENT << "int result;\n"; + s << INDENT << BEGIN_ALLOW_THREADS << Qt::endl; + s << INDENT << "result = !" << CPP_SELF_VAR << "->isNull();\n"; + s << INDENT << END_ALLOW_THREADS << Qt::endl; + s << INDENT << "return result;\n"; } else { - s << INDENT << "return !" << CPP_SELF_VAR << "->isNull();" << endl; + s << INDENT << "return !" << CPP_SELF_VAR << "->isNull();\n"; } - s << '}' << endl << endl; + s << "}\n\n"; } if (supportsNumberProtocol(metaClass) && !metaClass->typeEntry()->isSmartPointer()) { @@ -632,7 +693,7 @@ void CppGenerator::generateClass(QTextStream &s, GeneratorContext &classContext) } if (!metaClass->isNamespace() && metaClass->hasComparisonOperatorOverload()) { - s << "// Rich comparison" << endl; + s << "// Rich comparison\n"; writeRichCompareFunction(s, classContext); } @@ -644,28 +705,43 @@ void CppGenerator::generateClass(QTextStream &s, GeneratorContext &classContext) writeGetterFunction(s, metaField, classContext); if (canGenerateFieldSetter(metaField)) writeSetterFunction(s, metaField, classContext); - s << endl; + s << Qt::endl; } - s << "// Getters and Setters for " << metaClass->name() << endl; - s << "static PyGetSetDef " << cpythonGettersSettersDefinitionName(metaClass) << "[] = {" << endl; + for (const QPropertySpec *property : metaClass->propertySpecs()) { + if (property->generateGetSetDef() || !usePySideExtensions()) { + writeGetterFunction(s, property, classContext); + if (property->hasWrite()) + writeSetterFunction(s, property, classContext); + } + } + + s << "// Getters and Setters for " << metaClass->name() << Qt::endl; + s << "static PyGetSetDef " << cpythonGettersSettersDefinitionName(metaClass) << "[] = {\n"; for (const AbstractMetaField *metaField : fields) { - if (metaField->isStatic()) - continue; + if (!metaField->isStatic()) { + s << INDENT; + const QString setter = canGenerateFieldSetter(metaField) + ? cpythonSetterFunctionName(metaField) : QString(); + writePyGetSetDefEntry(s, metaField->name(), + cpythonGetterFunctionName(metaField), setter); + } + } - s << INDENT << "{const_cast<char *>(\"" << metaField->name() << "\"), "; - s << cpythonGetterFunctionName(metaField) << ", "; - if (canGenerateFieldSetter(metaField)) - s << cpythonSetterFunctionName(metaField); - else - s << NULL_PTR; - s << "}," << endl; + for (const QPropertySpec *property : metaClass->propertySpecs()) { + if (property->generateGetSetDef() || !usePySideExtensions()) { + s << INDENT; + const QString setter = property->hasWrite() + ? cpythonSetterFunctionName(property, metaClass) : QString(); + writePyGetSetDefEntry(s, property->name(), + cpythonGetterFunctionName(property, metaClass), setter); + } } - s << INDENT << '{' << NULL_PTR << "} // Sentinel" << endl; - s << "};" << endl << endl; + s << INDENT << '{' << NULL_PTR << "} // Sentinel\n"; + s << "};\n\n"; } - s << "} // extern \"C\"" << endl << endl; + s << "} // extern \"C\"\n\n"; if (!metaClass->typeEntry()->hashFunction().isEmpty()) writeHashFunction(s, classContext); @@ -675,58 +751,66 @@ void CppGenerator::generateClass(QTextStream &s, GeneratorContext &classContext) writeTpClearFunction(s, metaClass); writeClassDefinition(s, metaClass, classContext); - s << endl; + s << Qt::endl; if (metaClass->isPolymorphic() && metaClass->baseClass()) writeTypeDiscoveryFunction(s, metaClass); - - for (AbstractMetaEnum *cppEnum : qAsConst(classEnums)) { - if (cppEnum->isAnonymous() || cppEnum->isPrivate()) - continue; - - bool hasFlags = cppEnum->typeEntry()->flags(); - if (hasFlags) { - writeFlagsMethods(s, cppEnum); - writeFlagsNumberMethodsDefinition(s, cppEnum); - s << endl; - } - } - s << endl; + writeFlagsNumberMethodsDefinitions(s, classEnums); + s << Qt::endl; writeConverterFunctions(s, metaClass, classContext); writeClassRegister(s, metaClass, classContext, signatureStream); // class inject-code native/end if (!metaClass->typeEntry()->codeSnips().isEmpty()) { - writeCodeSnips(s, metaClass->typeEntry()->codeSnips(), TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode, metaClass); - s << endl; + writeClassCodeSnips(s, metaClass->typeEntry()->codeSnips(), + TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode, + classContext); + s << Qt::endl; } } -void CppGenerator::writeConstructorNative(QTextStream &s, const AbstractMetaFunction *func) +void CppGenerator::writeCacheResetNative(QTextStream &s, const GeneratorContext &classContext) { Indentation indentation(INDENT); - s << functionSignature(func, wrapperName(func->ownerClass()) + QLatin1String("::"), QString(), + s << "void " << classContext.wrapperName() + << "::resetPyMethodCache()\n{\n"; + s << INDENT << "std::fill_n(m_PyMethodCache, sizeof(m_PyMethodCache) / sizeof(m_PyMethodCache[0]), false);\n"; + s << "}\n\n"; +} + +void CppGenerator::writeConstructorNative(QTextStream &s, const GeneratorContext &classContext, + const AbstractMetaFunction *func) +{ + Indentation indentation(INDENT); + const QString qualifiedName = classContext.wrapperName() + QLatin1String("::"); + s << functionSignature(func, qualifiedName, QString(), OriginalTypeDescription | SkipDefaultValues); s << " : "; writeFunctionCall(s, func); - s << endl << "{" << endl; + s << "\n{\n"; + if (wrapperDiagnostics()) + s << INDENT << R"(std::cerr << __FUNCTION__ << ' ' << this << '\n';)" << '\n'; const AbstractMetaArgument *lastArg = func->arguments().isEmpty() ? nullptr : func->arguments().constLast(); + s << INDENT << "resetPyMethodCache();\n"; writeCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionBeginning, TypeSystem::NativeCode, func, lastArg); - s << INDENT << "// ... middle" << endl; + s << INDENT << "// ... middle\n"; writeCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode, func, lastArg); - s << '}' << endl << endl; + s << "}\n\n"; } -void CppGenerator::writeDestructorNative(QTextStream &s, const AbstractMetaClass *metaClass) +void CppGenerator::writeDestructorNative(QTextStream &s, const GeneratorContext &classContext) { Indentation indentation(INDENT); - s << wrapperName(metaClass) << "::~" << wrapperName(metaClass) << "()" << endl << '{' << endl; + s << classContext.wrapperName() << "::~" + << classContext.wrapperName() << "()\n{\n"; + if (wrapperDiagnostics()) + s << INDENT << R"(std::cerr << __FUNCTION__ << ' ' << this << '\n';)" << '\n'; // kill pyobject - s << INDENT << "SbkObject *wrapper = Shiboken::BindingManager::instance().retrieveWrapper(this);" << endl; - s << INDENT << "Shiboken::Object::destroy(wrapper, this);" << endl; - s << '}' << endl; + s << INDENT << "SbkObject *wrapper = Shiboken::BindingManager::instance().retrieveWrapper(this);\n"; + s << INDENT << "Shiboken::Object::destroy(wrapper, this);\n"; + s << "}\n"; } static bool allArgumentsRemoved(const AbstractMetaFunction *func) @@ -750,11 +834,14 @@ QString CppGenerator::getVirtualFunctionReturnTypeName(const AbstractMetaFunctio return QLatin1Char('"') + func->typeReplaced(0) + QLatin1Char('"'); // SbkType would return null when the type is a container. - if (func->type()->typeEntry()->isContainer()) { + auto typeEntry = func->type()->typeEntry(); + if (typeEntry->isContainer()) { return QLatin1Char('"') - + reinterpret_cast<const ContainerTypeEntry *>(func->type()->typeEntry())->typeName() + + reinterpret_cast<const ContainerTypeEntry *>(typeEntry)->typeName() + QLatin1Char('"'); } + if (typeEntry->isSmartPointer()) + return QLatin1Char('"') + typeEntry->qualifiedCppName() + QLatin1Char('"'); if (avoidProtectedHack()) { const AbstractMetaEnum *metaEnum = findAbstractMetaEnum(func->type()); @@ -765,134 +852,203 @@ QString CppGenerator::getVirtualFunctionReturnTypeName(const AbstractMetaFunctio if (func->type()->isPrimitive()) return QLatin1Char('"') + func->type()->name() + QLatin1Char('"'); - return QString::fromLatin1("reinterpret_cast<PyTypeObject *>(Shiboken::SbkType< %1 >())->tp_name").arg(func->type()->typeEntry()->qualifiedCppName()); + return QLatin1String("reinterpret_cast<PyTypeObject *>(Shiboken::SbkType< ") + + typeEntry->qualifiedCppName() + QLatin1String(" >())->tp_name"); +} + +// When writing an overridden method of a wrapper class, write the part +// calling the C++ function in case no overload in Python exists. +void CppGenerator::writeVirtualMethodCppCall(QTextStream &s, + const AbstractMetaFunction *func, + const QString &funcName, + const CodeSnipList &snips, + const AbstractMetaArgument *lastArg, + const TypeEntry *retType, + const QString &returnStatement) +{ + if (!snips.isEmpty()) { + writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, + TypeSystem::ShellCode, func, lastArg); + } + + if (func->isAbstract()) { + s << INDENT << "PyErr_SetString(PyExc_NotImplementedError, \"pure virtual method '" + << func->ownerClass()->name() << '.' << funcName + << "()' not implemented.\");\n" + << INDENT << returnStatement << '\n'; + return; + } + + s << INDENT; + if (retType) + s << "return "; + s << "this->::" << func->implementingClass()->qualifiedCppName() << "::"; + writeFunctionCall(s, func, Generator::VirtualCall); + s << ";\n"; + if (retType) + return; + if (!snips.isEmpty()) { + writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, + TypeSystem::ShellCode, func, lastArg); + } + s << INDENT << "return;\n"; } -void CppGenerator::writeVirtualMethodNative(QTextStream &s, const AbstractMetaFunction *func) +// Determine the return statement (void or a result value). +QString CppGenerator::virtualMethodReturn(QTextStream &s, + const AbstractMetaFunction *func, + const FunctionModificationList &functionModifications) +{ + if (func->isVoid()) + return QLatin1String("return;"); + 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+)")); + Q_ASSERT(regex.isValid()); + QString expr = argMod.replacedDefaultExpression; + for (int offset = 0; ; ) { + const QRegularExpressionMatch match = regex.match(expr, offset); + if (!match.hasMatch()) + break; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + const int argId = match.capturedView(1).toInt() - 1; +#else + const int argId = match.capturedRef(1).toInt() - 1; +#endif + if (argId < 0 || argId > func->arguments().count()) { + qCWarning(lcShiboken, "The expression used in return value contains an invalid index."); + break; + } + expr.replace(match.captured(0), func->arguments().at(argId)->name()); + offset = match.capturedStart(1); + } + DefaultValue defaultReturnExpr(DefaultValue::Custom, expr); + return QLatin1String("return ") + defaultReturnExpr.returnValue() + + QLatin1Char(';'); + } + } + } + const DefaultValue defaultReturnExpr = minimalConstructor(returnType); + if (!defaultReturnExpr.isValid()) { + QString errorMsg = QLatin1String(__FUNCTION__) + QLatin1String(": "); + if (const AbstractMetaClass *c = func->implementingClass()) + errorMsg += c->qualifiedCppName() + QLatin1String("::"); + errorMsg += func->signature(); + errorMsg = msgCouldNotFindMinimalConstructor(errorMsg, func->type()->cppSignature()); + qCWarning(lcShiboken).noquote().nospace() << errorMsg; + s << Qt::endl << INDENT << "#error " << errorMsg << Qt::endl; + } + if (returnType->referenceType() == LValueReference) { + s << INDENT << "static " << returnType->typeEntry()->qualifiedCppName() + << " result;\n"; + return QLatin1String("return result;"); + } + return QLatin1String("return ") + defaultReturnExpr.returnValue() + + QLatin1Char(';'); +} + +void CppGenerator::writeVirtualMethodNative(QTextStream &s, + const AbstractMetaFunction *func, + int cacheIndex) { //skip metaObject function, this will be written manually ahead if (usePySideExtensions() && func->ownerClass() && func->ownerClass()->isQObject() && ((func->name() == QLatin1String("metaObject")) || (func->name() == QLatin1String("qt_metacall")))) return; - const TypeEntry *retType = func->type() ? func->type()->typeEntry() : nullptr; + const TypeEntry *retType = func->type()->typeEntry(); const QString funcName = func->isOperatorOverload() ? pythonOperatorFunctionName(func) : func->name(); QString prefix = wrapperName(func->ownerClass()) + QLatin1String("::"); s << functionSignature(func, prefix, QString(), Generator::SkipDefaultValues|Generator::OriginalTypeDescription) - << endl << '{' << endl; + << "\n{\n"; Indentation indentation(INDENT); - DefaultValue defaultReturnExpr; - if (retType) { - const FunctionModificationList &mods = func->modifications(); - for (const FunctionModification &mod : mods) { - for (const ArgumentModification &argMod : mod.argument_mods) { - if (argMod.index == 0 && !argMod.replacedDefaultExpression.isEmpty()) { - static const QRegularExpression regex(QStringLiteral("%(\\d+)")); - Q_ASSERT(regex.isValid()); - QString expr = argMod.replacedDefaultExpression; - for (int offset = 0; ; ) { - const QRegularExpressionMatch match = regex.match(expr, offset); - if (!match.hasMatch()) - break; - const int argId = match.capturedRef(1).toInt() - 1; - if (argId < 0 || argId > func->arguments().count()) { - qCWarning(lcShiboken) << "The expression used in return value contains an invalid index."; - break; - } - expr.replace(match.captured(0), func->arguments().at(argId)->name()); - offset = match.capturedStart(1); - } - defaultReturnExpr.setType(DefaultValue::Custom); - defaultReturnExpr.setValue(expr); - } - } - } - if (!defaultReturnExpr.isValid()) - defaultReturnExpr = minimalConstructor(func->type()); - if (!defaultReturnExpr.isValid()) { - QString errorMsg = QLatin1String(__FUNCTION__) + QLatin1String(": "); - if (const AbstractMetaClass *c = func->implementingClass()) - errorMsg += c->qualifiedCppName() + QLatin1String("::"); - errorMsg += func->signature(); - errorMsg = msgCouldNotFindMinimalConstructor(errorMsg, func->type()->cppSignature()); - qCWarning(lcShiboken).noquote().nospace() << errorMsg; - s << endl << INDENT << "#error " << errorMsg << endl; - } - } else { - defaultReturnExpr.setType(DefaultValue::Void); - } + const FunctionModificationList &functionModifications = func->modifications(); + + const QString returnStatement = virtualMethodReturn(s, func, functionModifications); if (func->isAbstract() && func->isModifiedRemoved()) { qCWarning(lcShiboken).noquote().nospace() << QString::fromLatin1("Pure virtual method '%1::%2' must be implement but was "\ "completely removed on type system.") .arg(func->ownerClass()->name(), func->minimalSignature()); - s << INDENT << returnStatement(defaultReturnExpr.returnValue()) << endl; - s << '}' << endl << endl; + s << INDENT << returnStatement << "\n}\n\n"; return; } + const CodeSnipList snips = func->hasInjectedCode() + ? func->injectedCodeSnips() : CodeSnipList(); + const AbstractMetaArgument *lastArg = func->arguments().isEmpty() + ? nullptr : func->arguments().constLast(); + //Write declaration/native injected code - if (func->hasInjectedCode()) { - CodeSnipList snips = func->injectedCodeSnips(); - const AbstractMetaArgument *lastArg = func->arguments().isEmpty() ? nullptr : func->arguments().constLast(); - writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionDeclaration, TypeSystem::NativeCode, func, lastArg); - s << endl; + if (!snips.isEmpty()) { + writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionDeclaration, + TypeSystem::ShellCode, func, lastArg); } - s << INDENT << "Shiboken::GilState gil;" << endl; - - // Get out of virtual method call if someone already threw an error. - s << INDENT << "if (PyErr_Occurred())" << endl; + if (wrapperDiagnostics()) { + s << INDENT << "std::cerr << "; +#ifndef Q_CC_MSVC // g++ outputs __FUNCTION__ unqualified + s << '"' << prefix << R"(" << )"; +#endif + s << R"(__FUNCTION__ << ' ' << this << " m_PyMethodCache[" << )" + << cacheIndex << R"( << "]=" << m_PyMethodCache[)" << cacheIndex + << R"(] << '\n';)" << '\n'; + } + // PYSIDE-803: Build a boolean cache for unused overrides. + const bool multi_line = func->isVoid() || !snips.isEmpty() || func->isAbstract(); + s << INDENT << "if (m_PyMethodCache[" << cacheIndex << "])" << (multi_line ? " {\n" : "\n"); { Indentation indentation(INDENT); - s << INDENT << returnStatement(defaultReturnExpr.returnValue()) << endl; + writeVirtualMethodCppCall(s, func, funcName, snips, lastArg, retType, + returnStatement); } + if (multi_line) + s << INDENT << "}\n"; - s << INDENT << "Shiboken::AutoDecRef " << PYTHON_OVERRIDE_VAR << "(Shiboken::BindingManager::instance().getOverride(this, \""; - s << funcName << "\"));" << endl; - - s << INDENT << "if (" << PYTHON_OVERRIDE_VAR << ".isNull()) {" << endl; - { - Indentation indentation(INDENT); - CodeSnipList snips; - if (func->hasInjectedCode()) { - snips = func->injectedCodeSnips(); - const AbstractMetaArgument *lastArg = func->arguments().isEmpty() ? nullptr : func->arguments().constLast(); - writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::ShellCode, func, lastArg); - s << endl; - } + s << INDENT << "Shiboken::GilState gil;\n"; - if (func->isAbstract()) { - s << INDENT << "PyErr_SetString(PyExc_NotImplementedError, \"pure virtual method '"; - s << func->ownerClass()->name() << '.' << funcName; - s << "()' not implemented.\");" << endl; - s << INDENT << "return"; - if (retType) - s << ' ' << defaultReturnExpr.returnValue(); - } else { - s << INDENT << "gil.release();" << endl; - s << INDENT; - if (retType) - s << "return "; - s << "this->::" << func->implementingClass()->qualifiedCppName() << "::"; - writeFunctionCall(s, func, Generator::VirtualCall); - if (!retType) - s << ";\n" << INDENT << "return"; - } - } - s << ';' << endl; - s << INDENT << '}' << endl << endl; + // Get out of virtual method call if someone already threw an error. + s << INDENT << "if (PyErr_Occurred())\n" << indent(INDENT) + << INDENT << returnStatement << '\n' << outdent(INDENT); + + //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) + QLatin1Char(':'); + + s << INDENT << "static PyObject *nameCache[2] = {};\n"; + if (propFlag) + s << INDENT << "// This method belongs to a property.\n"; + s << INDENT << "static const char *funcName = \"" << propStr << funcName << "\";\n"; + s << INDENT << "Shiboken::AutoDecRef " << PYTHON_OVERRIDE_VAR + << "(Shiboken::BindingManager::instance().getOverride(this, nameCache, funcName));\n"; + s << INDENT << "if (" << PYTHON_OVERRIDE_VAR << ".isNull()) {\n" + << indent(INDENT) << INDENT << "gil.release();\n"; + if (useOverrideCaching(func->ownerClass())) + s << INDENT << "m_PyMethodCache[" << cacheIndex << "] = true;\n"; + writeVirtualMethodCppCall(s, func, funcName, snips, lastArg, retType, + returnStatement); + s << outdent(INDENT) << INDENT << "}\n\n"; //WS writeConversionRule(s, func, TypeSystem::TargetLangCode); s << INDENT << "Shiboken::AutoDecRef " << PYTHON_ARGS << "("; if (func->arguments().isEmpty() || allArgumentsRemoved(func)) { - s << "PyTuple_New(0));" << endl; + s << "PyTuple_New(0));\n"; } else { QStringList argConversions; const AbstractMetaArgumentList &arguments = func->arguments(); @@ -918,8 +1074,9 @@ void CppGenerator::writeVirtualMethodNative(QTextStream &s, const AbstractMetaFu convert = !m_formatUnits.contains(argType->name()); } - Indentation indentation(INDENT); - ac << INDENT; + Indentor nested; + Indentation indentation(nested); + ac << nested; if (!func->conversionRule(TypeSystem::TargetLangCode, arg->argumentIndex() + 1).isEmpty()) { // Has conversion rule. ac << arg->name() + QLatin1String(CONV_RULE_OUT_VAR_SUFFIX); @@ -934,95 +1091,90 @@ void CppGenerator::writeVirtualMethodNative(QTextStream &s, const AbstractMetaFu argConversions << argConv; } - s << "Py_BuildValue(\"(" << getFormatUnitString(func, false) << ")\"," << endl; - s << argConversions.join(QLatin1String(",\n")) << endl; - s << INDENT << "));" << endl; + s << "Py_BuildValue(\"(" << getFormatUnitString(func, false) << ")\",\n"; + s << argConversions.join(QLatin1String(",\n")) << Qt::endl; + s << INDENT << "));\n"; } bool invalidateReturn = false; QSet<int> invalidateArgs; - const FunctionModificationList &mods = func->modifications(); - for (const FunctionModification &funcMod : mods) { + for (const FunctionModification &funcMod : functionModifications) { for (const ArgumentModification &argMod : funcMod.argument_mods) { if (argMod.resetAfterUse && !invalidateArgs.contains(argMod.index)) { invalidateArgs.insert(argMod.index); s << INDENT << "bool invalidateArg" << argMod.index; - s << " = PyTuple_GET_ITEM(" << PYTHON_ARGS << ", " << argMod.index - 1 << ")->ob_refcnt == 1;" << endl; + s << " = PyTuple_GET_ITEM(" << PYTHON_ARGS << ", " << argMod.index - 1 << ")->ob_refcnt == 1;\n"; } else if (argMod.index == 0 && argMod.ownerships[TypeSystem::TargetLangCode] == TypeSystem::CppOwnership) { invalidateReturn = true; } } } - s << endl; - - CodeSnipList snips; - if (func->hasInjectedCode()) { - snips = func->injectedCodeSnips(); + s << Qt::endl; + if (!snips.isEmpty()) { if (injectedCodeUsesPySelf(func)) - s << INDENT << "PyObject *pySelf = BindingManager::instance().retrieveWrapper(this);" << endl; + s << INDENT << "PyObject *pySelf = BindingManager::instance().retrieveWrapper(this);\n"; const AbstractMetaArgument *lastArg = func->arguments().isEmpty() ? nullptr : func->arguments().constLast(); writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::NativeCode, func, lastArg); - s << endl; } if (!injectedCodeCallsPythonOverride(func)) { s << INDENT; s << "Shiboken::AutoDecRef " << PYTHON_RETURN_VAR << "(PyObject_Call(" - << PYTHON_OVERRIDE_VAR << ", " << PYTHON_ARGS << ", nullptr));" << endl; + << PYTHON_OVERRIDE_VAR << ", " << PYTHON_ARGS << ", nullptr));\n"; - s << INDENT << "// An error happened in python code!" << endl; - s << INDENT << "if (" << PYTHON_RETURN_VAR << ".isNull()) {" << endl; + s << INDENT << "// An error happened in python code!\n"; + s << INDENT << "if (" << PYTHON_RETURN_VAR << ".isNull()) {\n"; { Indentation indent(INDENT); - s << INDENT << "PyErr_Print();" << endl; - s << INDENT << returnStatement(defaultReturnExpr.returnValue()) << endl; + s << INDENT << "PyErr_Print();\n"; + s << INDENT << returnStatement << '\n'; } - s << INDENT << '}' << endl; + s << INDENT << "}\n"; - if (retType) { + if (!func->isVoid()) { if (invalidateReturn) - s << INDENT << "bool invalidateArg0 = " << PYTHON_RETURN_VAR << "->ob_refcnt == 1;" << endl; + s << INDENT << "bool invalidateArg0 = " << PYTHON_RETURN_VAR << "->ob_refcnt == 1;\n"; if (func->typeReplaced(0) != QLatin1String("PyObject")) { - s << INDENT << "// Check return type" << endl; + s << INDENT << "// Check return type\n"; s << INDENT; if (func->typeReplaced(0).isEmpty()) { s << "PythonToCppFunc " << PYTHON_TO_CPP_VAR << " = " << cpythonIsConvertibleFunction(func->type()); - s << PYTHON_RETURN_VAR << ");" << endl; - s << INDENT << "if (!" << PYTHON_TO_CPP_VAR << ") {" << endl; + s << PYTHON_RETURN_VAR << ");\n"; + s << INDENT << "if (!" << PYTHON_TO_CPP_VAR << ") {\n"; { Indentation indent(INDENT); s << INDENT << "Shiboken::warning(PyExc_RuntimeWarning, 2, "\ "\"Invalid return value in function %s, expected %s, got %s.\", \""; s << func->ownerClass()->name() << '.' << funcName << "\", " << getVirtualFunctionReturnTypeName(func); - s << ", Py_TYPE(" << PYTHON_RETURN_VAR << ")->tp_name);" << endl; - s << INDENT << returnStatement(defaultReturnExpr.returnValue()) << endl; + s << ", Py_TYPE(" << PYTHON_RETURN_VAR << ")->tp_name);\n"; + s << INDENT << returnStatement << '\n'; } - s << INDENT << '}' << endl; + s << INDENT << "}\n"; } else { - s << INDENT << "// Check return type" << endl; + s << INDENT << "// Check return type\n"; s << INDENT << "bool typeIsValid = "; writeTypeCheck(s, func->type(), QLatin1String(PYTHON_RETURN_VAR), isNumber(func->type()->typeEntry()), func->typeReplaced(0)); - s << ';' << endl; + s << ";\n"; s << INDENT << "if (!typeIsValid"; if (isPointerToWrapperType(func->type())) s << " && " << PYTHON_RETURN_VAR << " != Py_None"; - s << ") {" << endl; + s << ") {\n"; { Indentation indent(INDENT); s << INDENT << "Shiboken::warning(PyExc_RuntimeWarning, 2, "\ "\"Invalid return value in function %s, expected %s, got %s.\", \""; s << func->ownerClass()->name() << '.' << funcName << "\", " << getVirtualFunctionReturnTypeName(func); - s << ", Py_TYPE(" << PYTHON_RETURN_VAR << ")->tp_name);" << endl; - s << INDENT << returnStatement(defaultReturnExpr.returnValue()) << endl; + s << ", Py_TYPE(" << PYTHON_RETURN_VAR << ")->tp_name);\n"; + s << INDENT << returnStatement << '\n'; } - s << INDENT << '}' << endl; + s << INDENT << "}\n"; } } @@ -1038,37 +1190,35 @@ void CppGenerator::writeVirtualMethodNative(QTextStream &s, const AbstractMetaFu } if (invalidateReturn) { - s << INDENT << "if (invalidateArg0)" << endl; - Indentation indentation(INDENT); - s << INDENT << "Shiboken::Object::releaseOwnership(" << PYTHON_RETURN_VAR << ".object());" << endl; + s << INDENT << "if (invalidateArg0)\n" << indent(INDENT) + << INDENT << "Shiboken::Object::releaseOwnership(" << PYTHON_RETURN_VAR + << ".object());\n" << outdent(INDENT); } for (int argIndex : qAsConst(invalidateArgs)) { - s << INDENT << "if (invalidateArg" << argIndex << ')' << endl; - Indentation indentation(INDENT); - s << INDENT << "Shiboken::Object::invalidate(PyTuple_GET_ITEM(" << PYTHON_ARGS << ", "; - s << (argIndex - 1) << "));" << endl; + s << INDENT << "if (invalidateArg" << argIndex << ")\n" << indent(INDENT) + << INDENT << "Shiboken::Object::invalidate(PyTuple_GET_ITEM(" << PYTHON_ARGS + << ", " << (argIndex - 1) << "));\n" << outdent(INDENT); } - const FunctionModificationList &funcMods = func->modifications(); - for (const FunctionModification &funcMod : funcMods) { + for (const FunctionModification &funcMod : functionModifications) { for (const ArgumentModification &argMod : funcMod.argument_mods) { if (argMod.ownerships.contains(TypeSystem::NativeCode) && argMod.index == 0 && argMod.ownerships[TypeSystem::NativeCode] == TypeSystem::CppOwnership) { - s << INDENT << "if (Shiboken::Object::checkType(" << PYTHON_RETURN_VAR << "))" << endl; + s << INDENT << "if (Shiboken::Object::checkType(" << PYTHON_RETURN_VAR << "))\n"; Indentation indent(INDENT); - s << INDENT << "Shiboken::Object::releaseOwnership(" << PYTHON_RETURN_VAR << ");" << endl; + s << INDENT << "Shiboken::Object::releaseOwnership(" << PYTHON_RETURN_VAR << ");\n"; } } } if (func->hasInjectedCode()) { - s << endl; + s << Qt::endl; const AbstractMetaArgument *lastArg = func->arguments().isEmpty() ? nullptr : func->arguments().constLast(); writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode, func, lastArg); } - if (retType) { + if (!func->isVoid()) { s << INDENT << "return "; if (avoidProtectedHack() && retType->isEnum()) { const AbstractMetaEnum *metaEnum = findAbstractMetaEnum(retType); @@ -1083,32 +1233,33 @@ void CppGenerator::writeVirtualMethodNative(QTextStream &s, const AbstractMetaFu } if (func->type()->referenceType() == LValueReference && !isPointer(func->type())) s << " *"; - s << CPP_RETURN_VAR << ';' << endl; + s << CPP_RETURN_VAR << ";\n"; } - s << '}' << endl << endl; + s<< "}\n\n"; } -void CppGenerator::writeMetaObjectMethod(QTextStream &s, const AbstractMetaClass *metaClass) +void CppGenerator::writeMetaObjectMethod(QTextStream &s, const GeneratorContext &classContext) { Indentation indentation(INDENT); - QString wrapperClassName = wrapperName(metaClass); - s << "const QMetaObject *" << wrapperClassName << "::metaObject() const" << endl; - s << '{' << endl; - s << INDENT << "if (QObject::d_ptr->metaObject)" << endl - << INDENT << INDENT << "return QObject::d_ptr->dynamicMetaObject();" << endl; - s << INDENT << "SbkObject *pySelf = Shiboken::BindingManager::instance().retrieveWrapper(this);" << endl; - s << INDENT << "if (pySelf == nullptr)" << endl; - s << INDENT << INDENT << "return " << metaClass->qualifiedCppName() << "::metaObject();" << endl; - s << INDENT << "return PySide::SignalManager::retrieveMetaObject(reinterpret_cast<PyObject *>(pySelf));" << endl; - s << '}' << endl << endl; + 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" + << INDENT << INDENT << "return QObject::d_ptr->dynamicMetaObject();\n"; + s << INDENT << "SbkObject *pySelf = Shiboken::BindingManager::instance().retrieveWrapper(this);\n"; + s << INDENT << "if (pySelf == nullptr)\n"; + s << INDENT << INDENT << "return " << qualifiedCppName << "::metaObject();\n"; + s << INDENT << "return PySide::SignalManager::retrieveMetaObject(reinterpret_cast<PyObject *>(pySelf));\n"; + s<< "}\n\n"; // qt_metacall function - s << "int " << wrapperClassName << "::qt_metacall(QMetaObject::Call call, int id, void **args)" << endl; - s << "{" << endl; + s << "int " << wrapperClassName << "::qt_metacall(QMetaObject::Call call, int id, void **args)\n"; + s << "{\n"; AbstractMetaFunction *func = nullptr; - AbstractMetaFunctionList list = metaClass->queryFunctionsByName(QLatin1String("qt_metacall")); + AbstractMetaFunctionList list = + classContext.metaClass()->queryFunctionsByName(QLatin1String("qt_metacall")); if (list.size() == 1) func = list[0]; @@ -1121,26 +1272,26 @@ void CppGenerator::writeMetaObjectMethod(QTextStream &s, const AbstractMetaClass } } - s << INDENT << "int result = " << metaClass->qualifiedCppName() << "::qt_metacall(call, id, args);" << endl; - s << INDENT << "return result < 0 ? result : PySide::SignalManager::qt_metacall(this, call, id, args);" << endl; - s << "}" << endl << endl; + s << INDENT << "int result = " << qualifiedCppName << "::qt_metacall(call, id, args);\n"; + s << INDENT << "return result < 0 ? result : PySide::SignalManager::qt_metacall(this, call, id, args);\n"; + s << "}\n\n"; // qt_metacast function - writeMetaCast(s, metaClass); + writeMetaCast(s, classContext); } -void CppGenerator::writeMetaCast(QTextStream &s, const AbstractMetaClass *metaClass) +void CppGenerator::writeMetaCast(QTextStream &s, const GeneratorContext &classContext) { Indentation indentation(INDENT); - QString wrapperClassName = wrapperName(metaClass); - s << "void *" << wrapperClassName << "::qt_metacast(const char *_clname)" << endl; - s << '{' << endl; - s << INDENT << "if (!_clname) return {};" << endl; - s << INDENT << "SbkObject *pySelf = Shiboken::BindingManager::instance().retrieveWrapper(this);" << endl; - s << INDENT << "if (pySelf && PySide::inherits(Py_TYPE(pySelf), _clname))" << endl; - s << INDENT << INDENT << "return static_cast<void *>(const_cast< " << wrapperClassName << " *>(this));" << endl; - s << INDENT << "return " << metaClass->qualifiedCppName() << "::qt_metacast(_clname);" << endl; - s << "}" << endl << endl; + const QString wrapperClassName = classContext.wrapperName(); + const QString qualifiedCppName = classContext.metaClass()->qualifiedCppName(); + s << "void *" << wrapperClassName << "::qt_metacast(const char *_clname)\n{\n"; + s << INDENT << "if (!_clname) return {};\n"; + s << INDENT << "SbkObject *pySelf = Shiboken::BindingManager::instance().retrieveWrapper(this);\n"; + s << INDENT << "if (pySelf && PySide::inherits(Py_TYPE(pySelf), _clname))\n"; + s << INDENT << INDENT << "return static_cast<void *>(const_cast< " << wrapperClassName << " *>(this));\n"; + s << INDENT << "return " << qualifiedCppName << "::qt_metacast(_clname);\n"; + s << "}\n\n"; } void CppGenerator::writeEnumConverterFunctions(QTextStream &s, const AbstractMetaEnum *metaEnum) @@ -1164,13 +1315,14 @@ void CppGenerator::writeEnumConverterFunctions(QTextStream &s, const TypeEntry * } QString code; QTextStream c(&code); - c << INDENT << "*reinterpret_cast<" << cppTypeName << " *>(cppOut) =\n" - << INDENT << " "; + Indentor nested; + c << nested << "*reinterpret_cast<" << cppTypeName << " *>(cppOut) =\n" + << nested << " "; if (enumType->isFlags()) c << cppTypeName << "(QFlag(int(PySide::QFlags::getValue(reinterpret_cast<PySideQFlagsObject *>(pyIn)))))"; else c << "static_cast<" << cppTypeName << ">(Shiboken::Enum::getValue(pyIn))"; - c << ';' << endl; + c << ";\n"; writePythonToCppFunction(s, code, typeName, typeName); QString pyTypeCheck = QStringLiteral("PyObject_TypeCheck(pyIn, %1)").arg(enumPythonType); @@ -1178,9 +1330,9 @@ void CppGenerator::writeEnumConverterFunctions(QTextStream &s, const TypeEntry * code.clear(); - c << INDENT << "const int castCppIn = int(*reinterpret_cast<const " - << cppTypeName << " *>(cppIn));" << endl; - c << INDENT; + c << nested << "const int castCppIn = int(*reinterpret_cast<const " + << cppTypeName << " *>(cppIn));\n"; + c << nested; c << "return "; if (enumType->isFlags()) { c << "reinterpret_cast<PyObject *>(PySide::QFlags::newObject(castCppIn, " @@ -1188,9 +1340,9 @@ void CppGenerator::writeEnumConverterFunctions(QTextStream &s, const TypeEntry * } else { c << "Shiboken::Enum::newItem(" << enumPythonType << ", castCppIn)"; } - c << ';' << endl; + c << ";\n"; writeCppToPythonFunction(s, code, typeName, typeName); - s << endl; + s << Qt::endl; if (enumType->isFlags()) return; @@ -1205,19 +1357,19 @@ void CppGenerator::writeEnumConverterFunctions(QTextStream &s, const TypeEntry * code.clear(); cppTypeName = getFullTypeName(flags).trimmed(); - c << INDENT << "*reinterpret_cast<" << cppTypeName << " *>(cppOut) =\n" - << INDENT << " " << cppTypeName - << "(QFlag(int(Shiboken::Enum::getValue(pyIn))));" << endl; + c << nested << "*reinterpret_cast<" << cppTypeName << " *>(cppOut) =\n" + << nested << " " << cppTypeName + << "(QFlag(int(Shiboken::Enum::getValue(pyIn))));\n"; QString flagsTypeName = fixedCppTypeName(flags); writePythonToCppFunction(s, code, typeName, flagsTypeName); writeIsPythonConvertibleToCppFunction(s, typeName, flagsTypeName, pyTypeCheck); code.clear(); - c << INDENT << "Shiboken::AutoDecRef pyLong(PyNumber_Long(pyIn));" << endl; - c << INDENT << "*reinterpret_cast<" << cppTypeName << " *>(cppOut) =\n" - << INDENT << " " << cppTypeName - << "(QFlag(int(PyLong_AsLong(pyLong.object()))));" << endl; + c << nested << "Shiboken::AutoDecRef pyLong(PyNumber_Long(pyIn));\n"; + c << nested << "*reinterpret_cast<" << cppTypeName << " *>(cppOut) =\n" + << nested << " " << cppTypeName + << "(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, @@ -1232,16 +1384,14 @@ void CppGenerator::writeEnumConverterFunctions(QTextStream &s, const TypeEntry * } void CppGenerator::writeConverterFunctions(QTextStream &s, const AbstractMetaClass *metaClass, - GeneratorContext &classContext) + const GeneratorContext &classContext) { - s << "// Type conversion functions." << endl << endl; + s << "// Type conversion functions.\n\n"; AbstractMetaEnumList classEnums = metaClass->enums(); - const AbstractMetaClassList &innerClasses = metaClass->innerClasses(); - for (AbstractMetaClass *innerClass : innerClasses) - lookForEnumsInClassesNotToBeGenerated(classEnums, innerClass); + metaClass->getEnumsFromInvisibleNamespacesToBeGenerated(&classEnums); if (!classEnums.isEmpty()) - s << "// Python to C++ enum conversion." << endl; + s << "// Python to C++ enum conversion.\n"; for (const AbstractMetaEnum *metaEnum : qAsConst(classEnums)) writeEnumConverterFunctions(s, metaEnum); @@ -1257,62 +1407,63 @@ void CppGenerator::writeConverterFunctions(QTextStream &s, const AbstractMetaCla QString cpythonType = cpythonTypeName(metaClass); // Returns the C++ pointer of the Python wrapper. - s << "// Python to C++ pointer conversion - returns the C++ object of the Python wrapper (keeps object identity)." << endl; + s << "// Python to C++ pointer conversion - returns the C++ object of the Python wrapper (keeps object identity).\n"; QString sourceTypeName = metaClass->name(); QString targetTypeName = metaClass->name() + QLatin1String("_PTR"); QString code; QTextStream c(&code); - c << INDENT << "Shiboken::Conversions::pythonToCppPointer(" << cpythonType << ", pyIn, cppOut);"; + Indentor nested; + c << nested << "Shiboken::Conversions::pythonToCppPointer(" << cpythonType << ", pyIn, cppOut);"; writePythonToCppFunction(s, code, sourceTypeName, targetTypeName); // "Is convertible" function for the Python object to C++ pointer conversion. const QString pyTypeCheck = QLatin1String("PyObject_TypeCheck(pyIn, reinterpret_cast<PyTypeObject *>(") + cpythonType + QLatin1String("))"); writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, pyTypeCheck, QString(), true); - s << endl; + s << Qt::endl; // 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)." << endl; + s << "// C++ to Python pointer conversion - tries to find the Python wrapper for the C++ object (keeps object identity).\n"; code.clear(); if (usePySideExtensions() && metaClass->isQObject()) { - c << INDENT << "return PySide::getWrapperForQObject(reinterpret_cast<" - << typeName << " *>(const_cast<void *>(cppIn)), " << cpythonType << ");" << endl; + c << nested << "return PySide::getWrapperForQObject(reinterpret_cast<" + << typeName << " *>(const_cast<void *>(cppIn)), " << cpythonType << ");\n"; } else { - c << INDENT << "auto pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(cppIn));" << endl; - c << INDENT << "if (pyOut) {" << endl; + c << nested << "auto pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(cppIn));\n"; + c << nested << "if (pyOut) {\n"; { - Indentation indent(INDENT); - c << INDENT << "Py_INCREF(pyOut);" << endl; - c << INDENT << "return pyOut;" << endl; - } - c << INDENT << '}' << endl; - c << INDENT << "bool changedTypeName = false;\n" - << INDENT << "auto tCppIn = reinterpret_cast<const " << typeName << " *>(cppIn);\n" - << INDENT << "const char *typeName = typeid(*tCppIn).name();\n" - << INDENT << "auto sbkType = Shiboken::ObjectType::typeForTypeName(typeName);\n" - << INDENT << "if (sbkType && Shiboken::ObjectType::hasSpecialCastFunction(sbkType)) {\n" - << INDENT << " typeName = typeNameOf(tCppIn);\n" - << INDENT << " changedTypeName = true;\n" - << INDENT << " }\n" - << INDENT << "PyObject *result = Shiboken::Object::newObject(" << cpythonType + Indentation indent(nested); + c << nested << "Py_INCREF(pyOut);\n"; + c << nested << "return pyOut;\n"; + } + c << nested << "}\n"; + c << nested << "bool changedTypeName = false;\n" + << nested << "auto tCppIn = reinterpret_cast<const " << typeName << " *>(cppIn);\n" + << nested << "const char *typeName = typeid(*tCppIn).name();\n" + << nested << "auto sbkType = Shiboken::ObjectType::typeForTypeName(typeName);\n" + << nested << "if (sbkType && Shiboken::ObjectType::hasSpecialCastFunction(sbkType)) {\n" + << nested << " typeName = typeNameOf(tCppIn);\n" + << nested << " changedTypeName = true;\n" + << nested << "}\n" + << nested << "PyObject *result = Shiboken::Object::newObject(" << cpythonType << ", const_cast<void *>(cppIn), false, /* exactType */ changedTypeName, typeName);\n" - << INDENT << "if (changedTypeName)\n" - << INDENT << " delete [] typeName;\n" - << INDENT << "return result;"; + << nested << "if (changedTypeName)\n" + << nested << " delete [] typeName;\n" + << nested << "return result;"; } std::swap(targetTypeName, sourceTypeName); writeCppToPythonFunction(s, code, sourceTypeName, targetTypeName); // The conversions for an Object Type end here. if (!metaClass->typeEntry()->isValue() && !metaClass->typeEntry()->isSmartPointer()) { - s << endl; + s << Qt::endl; return; } // Always copies C++ value (not pointer, and not reference) to a new Python wrapper. - s << endl << "// C++ to Python copy conversion." << endl; + s << Qt::endl << "// C++ to Python copy conversion.\n"; if (!classContext.forSmartPointer()) targetTypeName = metaClass->name(); else @@ -1323,19 +1474,21 @@ void CppGenerator::writeConverterFunctions(QTextStream &s, const AbstractMetaCla code.clear(); QString computedWrapperName; - if (!classContext.forSmartPointer()) - computedWrapperName = wrapperName(metaClass); - else - computedWrapperName = wrapperName(classContext.preciseType()); + if (!classContext.forSmartPointer()) { + computedWrapperName = classContext.useWrapper() + ? classContext.wrapperName() : metaClass->qualifiedCppName(); + } else { + computedWrapperName = classContext.smartPointerWrapperName(); + } - c << INDENT << "return Shiboken::Object::newObject(" << cpythonType + c << nested << "return Shiboken::Object::newObject(" << cpythonType << ", new ::" << computedWrapperName << "(*reinterpret_cast<const " << typeName << " *>(cppIn)), true, true);"; writeCppToPythonFunction(s, code, sourceTypeName, targetTypeName); - s << endl; + s << Qt::endl; // Python to C++ copy conversion. - s << "// Python to C++ copy conversion." << endl; + s << "// Python to C++ copy conversion.\n"; if (!classContext.forSmartPointer()) sourceTypeName = metaClass->name(); else @@ -1351,13 +1504,13 @@ void CppGenerator::writeConverterFunctions(QTextStream &s, const AbstractMetaCla else wrappedCPtrExpression = cpythonWrapperCPtr(classContext.preciseType(), pyInVariable); - c << INDENT << "*reinterpret_cast<" << typeName << " *>(cppOut) = *" + c << nested << "*reinterpret_cast<" << typeName << " *>(cppOut) = *" << wrappedCPtrExpression << ';'; writePythonToCppFunction(s, code, sourceTypeName, targetTypeName); // "Is convertible" function for the Python object to C++ value copy conversion. writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, pyTypeCheck); - s << endl; + s << Qt::endl; // User provided implicit conversions. CustomConversion *customConversion = metaClass->typeEntry()->customConversion(); @@ -1373,7 +1526,7 @@ void CppGenerator::writeConverterFunctions(QTextStream &s, const AbstractMetaCla } if (!implicitConvs.isEmpty()) - s << "// Implicit conversions." << endl; + s << "// Implicit conversions.\n"; AbstractMetaType *targetType = buildAbstractMetaTypeFromAbstractMetaClass(metaClass); for (const AbstractMetaFunction *conv : qAsConst(implicitConvs)) { @@ -1420,9 +1573,9 @@ void CppGenerator::writeConverterFunctions(QTextStream &s, const AbstractMetaCla || sourceType->typeEntry()->isEnum() || sourceType->typeEntry()->isFlags()) { QTextStream pc(&toCppPreConv); - pc << INDENT << getFullTypeNameWithoutModifiers(sourceType) << " cppIn"; + pc << nested << getFullTypeNameWithoutModifiers(sourceType) << " cppIn"; writeMinimalConstructorExpression(pc, sourceType); - pc << ';' << endl; + pc << ";\n"; writeToCppConversion(pc, sourceType, nullptr, QLatin1String("pyIn"), QLatin1String("cppIn")); pc << ';'; toCppConv.append(QLatin1String("cppIn")); @@ -1449,52 +1602,72 @@ void CppGenerator::writeCustomConverterFunctions(QTextStream &s, const CustomCon const CustomConversion::TargetToNativeConversions &toCppConversions = customConversion->targetToNativeConversions(); if (toCppConversions.isEmpty()) return; - s << "// Python to C++ conversions for type '" << customConversion->ownerType()->qualifiedCppName() << "'." << endl; + s << "// Python to C++ conversions for type '" << customConversion->ownerType()->qualifiedCppName() << "'.\n"; for (CustomConversion::TargetToNativeConversion *toNative : toCppConversions) writePythonToCppConversionFunctions(s, toNative, customConversion->ownerType()); - s << endl; + s << Qt::endl; } void CppGenerator::writeConverterRegister(QTextStream &s, const AbstractMetaClass *metaClass, - GeneratorContext &classContext) + const GeneratorContext &classContext) { if (metaClass->isNamespace()) return; - s << INDENT << "// Register Converter" << endl; + s << INDENT << "// Register Converter\n"; s << INDENT << "SbkConverter *converter = Shiboken::Conversions::createConverter("; - s << cpythonTypeName(metaClass) << ',' << endl; + s << cpythonTypeName(metaClass) << ',' << Qt::endl; { Indentation indent(INDENT); QString sourceTypeName = metaClass->name(); QString targetTypeName = sourceTypeName + QLatin1String("_PTR"); - s << INDENT << pythonToCppFunctionName(sourceTypeName, targetTypeName) << ',' << endl; - s << INDENT << convertibleToCppFunctionName(sourceTypeName, targetTypeName) << ',' << endl; + s << INDENT << pythonToCppFunctionName(sourceTypeName, targetTypeName) << ',' << Qt::endl; + s << INDENT << convertibleToCppFunctionName(sourceTypeName, targetTypeName) << ',' << Qt::endl; std::swap(targetTypeName, sourceTypeName); s << INDENT << cppToPythonFunctionName(sourceTypeName, targetTypeName); if (metaClass->typeEntry()->isValue() || metaClass->typeEntry()->isSmartPointer()) { - s << ',' << endl; + s << ',' << Qt::endl; sourceTypeName = metaClass->name() + QLatin1String("_COPY"); s << INDENT << cppToPythonFunctionName(sourceTypeName, targetTypeName); } } - s << ");" << endl; + s << ");\n"; + + s << Qt::endl; + + auto writeConversions = [&s, this](const QString &signature) + { + s << INDENT << "Shiboken::Conversions::registerConverterName(converter, \"" << signature << "\");\n"; + s << INDENT << "Shiboken::Conversions::registerConverterName(converter, \"" << signature << "*\");\n"; + s << INDENT << "Shiboken::Conversions::registerConverterName(converter, \"" << signature << "&\");\n"; + }; + + auto writeConversionsForType = [writeConversions](const QString &fullTypeName) + { + QStringList lst = fullTypeName.split(QLatin1String("::"), + Qt::SkipEmptyParts); + while (!lst.isEmpty()) { + QString signature = lst.join(QLatin1String("::")); + writeConversions(signature); + lst.removeFirst(); + } + }; - s << endl; - QStringList cppSignature; if (!classContext.forSmartPointer()) { - cppSignature = metaClass->qualifiedCppName().split(QLatin1String("::"), - QString::SkipEmptyParts); + writeConversionsForType(metaClass->qualifiedCppName()); } else { - cppSignature = classContext.preciseType()->cppSignature().split(QLatin1String("::"), - QString::SkipEmptyParts); - } - while (!cppSignature.isEmpty()) { - QString signature = cppSignature.join(QLatin1String("::")); - s << INDENT << "Shiboken::Conversions::registerConverterName(converter, \"" << signature << "\");" << endl; - s << INDENT << "Shiboken::Conversions::registerConverterName(converter, \"" << signature << "*\");" << endl; - s << INDENT << "Shiboken::Conversions::registerConverterName(converter, \"" << signature << "&\");" << endl; - cppSignature.removeFirst(); + const QString &smartPointerType = classContext.preciseType()->instantiations().at(0)->cppSignature(); + const QString &smartPointerName = classContext.preciseType()->typeEntry()->name(); + + QStringList lst = smartPointerType.split(QLatin1String("::"), + Qt::SkipEmptyParts); + while (!lst.isEmpty()) { + QString signature = lst.join(QLatin1String("::")); + writeConversions(QStringLiteral("%1<%2 >").arg(smartPointerName, signature)); + lst.removeFirst(); + } + + writeConversionsForType(smartPointerType); } s << INDENT << "Shiboken::Conversions::registerConverterName(converter, typeid(::"; @@ -1504,20 +1677,20 @@ void CppGenerator::writeConverterRegister(QTextStream &s, const AbstractMetaClas else qualifiedCppNameInvocation = classContext.preciseType()->cppSignature(); - s << qualifiedCppNameInvocation << ").name());" << endl; + s << qualifiedCppNameInvocation << ").name());\n"; - if (shouldGenerateCppWrapper(metaClass)) { + if (classContext.useWrapper()) { s << INDENT << "Shiboken::Conversions::registerConverterName(converter, typeid(::"; - s << wrapperName(metaClass) << ").name());" << endl; + s << classContext.wrapperName() << ").name());\n"; } - s << endl; + s << Qt::endl; if (!metaClass->typeEntry()->isValue() && !metaClass->typeEntry()->isSmartPointer()) return; // Python to C++ copy (value, not pointer neither reference) conversion. - s << INDENT << "// Add Python to C++ copy (value, not pointer neither reference) conversion to type converter." << endl; + s << INDENT << "// Add Python to C++ copy (value, not pointer neither reference) conversion to type converter.\n"; QString sourceTypeName = metaClass->name(); QString targetTypeName = sourceTypeName + QLatin1String("_COPY"); QString toCpp = pythonToCppFunctionName(sourceTypeName, targetTypeName); @@ -1538,7 +1711,7 @@ void CppGenerator::writeConverterRegister(QTextStream &s, const AbstractMetaClas } if (!implicitConvs.isEmpty()) - s << INDENT << "// Add implicit conversions to type converter." << endl; + s << INDENT << "// Add implicit conversions to type converter.\n"; AbstractMetaType *targetType = buildAbstractMetaTypeFromAbstractMetaClass(metaClass); for (const AbstractMetaFunction *conv : qAsConst(implicitConvs)) { @@ -1568,7 +1741,7 @@ void CppGenerator::writeCustomConverterRegister(QTextStream &s, const CustomConv const CustomConversion::TargetToNativeConversions &toCppConversions = customConversion->targetToNativeConversions(); if (toCppConversions.isEmpty()) return; - s << INDENT << "// Add user defined implicit conversions to type converter." << endl; + s << INDENT << "// Add user defined implicit conversions to type converter.\n"; for (CustomConversion::TargetToNativeConversion *toNative : toCppConversions) { QString toCpp = pythonToCppFunctionName(toNative, customConversion->ownerType()); QString isConv = convertibleToCppFunctionName(toNative, customConversion->ownerType()); @@ -1582,11 +1755,36 @@ void CppGenerator::writeContainerConverterFunctions(QTextStream &s, const Abstra writePythonToCppConversionFunctions(s, containerType); } +void CppGenerator::writeSmartPointerConverterFunctions(QTextStream &s, const AbstractMetaType *smartPointerType) +{ + const AbstractMetaClass *targetClass = AbstractMetaClass::findClass(classes(), smartPointerType->instantiations().at(0)->typeEntry()); + + if (targetClass) { + const auto *smartPointerTypeEntry = + static_cast<const SmartPointerTypeEntry *>( + smartPointerType->typeEntry()); + + // TODO: Missing conversion to smart pointer pointer type: + + s << "// Register smartpointer conversion for all derived classes\n"; + const auto classes = getBaseClasses(targetClass); + for (auto k : classes) { + if (smartPointerTypeEntry->matchesInstantiation(k->typeEntry())) { + if (auto smartTargetType = findSmartPointerInstantiation(k->typeEntry())) { + s << INDENT << "// SmartPointer derived class: " << smartTargetType->cppSignature() << "\n"; + writePythonToCppConversionFunctions(s, smartPointerType, smartTargetType, {}, {}, {}); + } + } + } + } +} + void CppGenerator::writeMethodWrapperPreamble(QTextStream &s, OverloadData &overloadData, - GeneratorContext &context) + const GeneratorContext &context) { const AbstractMetaFunction *rfunc = overloadData.referenceFunction(); - const AbstractMetaClass *ownerClass = rfunc->ownerClass(); + const AbstractMetaClass *ownerClass = rfunc->targetLangOwner(); + Q_ASSERT(ownerClass == context.metaClass()); int minArgs = overloadData.minArgs(); int maxArgs = overloadData.maxArgs(); bool initPythonArguments; @@ -1604,19 +1802,18 @@ void CppGenerator::writeMethodWrapperPreamble(QTextStream &s, OverloadData &over else qualifiedCppName = context.preciseType()->cppSignature(); - s << qualifiedCppName << " >()))" << endl; + s << qualifiedCppName << " >()))\n"; Indentation indent(INDENT); - s << INDENT << returnStatement(m_currentErrorCode) << endl << endl; + s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl << Qt::endl; } // Declare pointer for the underlying C++ object. s << INDENT << "::"; if (!context.forSmartPointer()) { - s << (shouldGenerateCppWrapper(ownerClass) ? wrapperName(ownerClass) - : ownerClass->qualifiedCppName()); + s << (context.useWrapper() ? context.wrapperName() : ownerClass->qualifiedCppName()); } else { - s << context.preciseType()->cppSignature(); + s << context.smartPointerWrapperName(); } - s << " *cptr{};" << endl; + s << " *cptr{};\n"; initPythonArguments = maxArgs > 0; usesNamedArguments = !ownerClass->isQObject() && overloadData.hasArgumentWithDefaultValue(); @@ -1627,40 +1824,42 @@ void CppGenerator::writeMethodWrapperPreamble(QTextStream &s, OverloadData &over writeCppSelfDefinition(s, rfunc, context, overloadData.hasStaticFunction()); } if (!rfunc->isInplaceOperator() && overloadData.hasNonVoidReturnType()) - s << INDENT << "PyObject *" << PYTHON_RETURN_VAR << "{};" << endl; + s << INDENT << "PyObject *" << PYTHON_RETURN_VAR << "{};\n"; initPythonArguments = minArgs != maxArgs || maxArgs > 1; usesNamedArguments = rfunc->isCallOperator() || overloadData.hasArgumentWithDefaultValue(); } + s << INDENT << "PyObject *errInfo{};\n"; + s << INDENT << "SBK_UNUSED(errInfo)\n"; + s << INDENT << "static const char *fullName = \"" + << fullPythonFunctionName(rfunc, true) << "\";\n"; + s << INDENT << "SBK_UNUSED(fullName)\n"; if (maxArgs > 0) { - s << INDENT << "int overloadId = -1;" << endl; + s << INDENT << "int overloadId = -1;\n"; s << INDENT << "PythonToCppFunc " << PYTHON_TO_CPP_VAR; if (pythonFunctionWrapperUsesListOfArguments(overloadData)) { s << "[] = { " << NULL_PTR; for (int i = 1; i < maxArgs; ++i) s << ", " << NULL_PTR; - s << " };" << endl; + s << " };\n"; } else { - s << "{};" << endl; + s << "{};\n"; } writeUnusedVariableCast(s, QLatin1String(PYTHON_TO_CPP_VAR)); } - if (usesNamedArguments && !rfunc->isCallOperator()) - s << INDENT << "int numNamedArgs = (kwds ? PyDict_Size(kwds) : 0);" << endl; - if (initPythonArguments) { - s << INDENT << "int numArgs = "; + s << INDENT << "const Py_ssize_t numArgs = "; if (minArgs == 0 && maxArgs == 1 && !rfunc->isConstructor() && !pythonFunctionWrapperUsesListOfArguments(overloadData)) - s << "(" << PYTHON_ARG << " == 0 ? 0 : 1);" << endl; + s << "(" << PYTHON_ARG << " == 0 ? 0 : 1);\n"; else writeArgumentsInitializer(s, overloadData); } } void CppGenerator::writeConstructorWrapper(QTextStream &s, const AbstractMetaFunctionList &overloads, - GeneratorContext &classContext) + const GeneratorContext &classContext) { ErrorCode errorCode(-1); OverloadData overloadData(overloads, this); @@ -1668,116 +1867,90 @@ void CppGenerator::writeConstructorWrapper(QTextStream &s, const AbstractMetaFun const AbstractMetaFunction *rfunc = overloadData.referenceFunction(); const AbstractMetaClass *metaClass = rfunc->ownerClass(); - s << "static int" << endl; - s << cpythonFunctionName(rfunc) << "(PyObject *self, PyObject *args, PyObject *kwds)" << endl; - s << '{' << endl; + s << "static int\n"; + s << cpythonFunctionName(rfunc) << "(PyObject *self, PyObject *args, PyObject *kwds)\n{\n"; - QSet<QString> argNamesSet; - if (usePySideExtensions() && metaClass->isQObject()) { - // Write argNames variable with all known argument names. - const OverloadData::MetaFunctionList &overloads = overloadData.overloads(); - for (const AbstractMetaFunction *func : overloads) { - const AbstractMetaArgumentList &arguments = func->arguments(); - for (const AbstractMetaArgument *arg : arguments) { - if (arg->defaultValueExpression().isEmpty() || func->argumentRemoved(arg->argumentIndex() + 1)) - continue; - argNamesSet << arg->name(); - } - } - QStringList argNamesList = argNamesSet.values(); - std::sort(argNamesList.begin(), argNamesList.end()); - if (argNamesList.isEmpty()) { - s << INDENT << "const char **argNames{};" << endl; - } else { - s << INDENT << "const char *argNames[] = {\"" - << argNamesList.join(QLatin1String("\", \"")) << "\"};" << endl; - } - s << INDENT << "const QMetaObject *metaObject;" << endl; - } + if (usePySideExtensions() && metaClass->isQObject()) + s << INDENT << "const QMetaObject *metaObject;\n"; - s << INDENT << "SbkObject *sbkSelf = reinterpret_cast<SbkObject *>(self);" << endl; + s << INDENT << "SbkObject *sbkSelf = reinterpret_cast<SbkObject *>(self);\n"; if (metaClass->isAbstract() || metaClass->baseClassNames().size() > 1) { - s << INDENT << "SbkObjectType *type = reinterpret_cast<SbkObjectType *>(self->ob_type);" << endl; - s << INDENT << "SbkObjectType *myType = reinterpret_cast<SbkObjectType *>(" << cpythonTypeNameExt(metaClass->typeEntry()) << ");" << endl; + s << INDENT << "SbkObjectType *type = reinterpret_cast<SbkObjectType *>(self->ob_type);\n"; + s << INDENT << "SbkObjectType *myType = reinterpret_cast<SbkObjectType *>(" << cpythonTypeNameExt(metaClass->typeEntry()) << ");\n"; } if (metaClass->isAbstract()) { - s << INDENT << "if (type == myType) {" << endl; - { - Indentation indent(INDENT); - s << INDENT << "PyErr_SetString(PyExc_NotImplementedError," << endl; - { - Indentation indentation(INDENT); - s << INDENT << "\"'" << metaClass->qualifiedCppName(); - } - s << "' represents a C++ abstract class and cannot be instantiated\");" << endl; - s << INDENT << returnStatement(m_currentErrorCode) << endl; - } - s << INDENT << '}' << endl << endl; + s << INDENT << "if (type == myType) {\n" << indent(INDENT) + << INDENT << "PyErr_SetString(PyExc_NotImplementedError,\n" << indent(INDENT) + << INDENT << "\"'" << metaClass->qualifiedCppName() + << "' represents a C++ abstract class and cannot be instantiated\");\n" << outdent(INDENT) + << INDENT << returnStatement(m_currentErrorCode) << '\n' << outdent(INDENT) + << INDENT<< "}\n\n"; } if (metaClass->baseClassNames().size() > 1) { - if (!metaClass->isAbstract()) { - s << INDENT << "if (type != myType) {" << endl; - } - { - Indentation indentation(INDENT); - s << INDENT << "Shiboken::ObjectType::copyMultipleInheritance(type, myType);" << endl; - } if (!metaClass->isAbstract()) - s << INDENT << '}' << endl << endl; + s << INDENT << "if (type != myType)\n" << indent(INDENT); + s << INDENT << "Shiboken::ObjectType::copyMultipleInheritance(type, myType);\n"; + if (!metaClass->isAbstract()) + s << outdent(INDENT) << '\n'; } writeMethodWrapperPreamble(s, overloadData, classContext); - s << endl; + s << Qt::endl; if (overloadData.maxArgs() > 0) writeOverloadedFunctionDecisor(s, overloadData); writeFunctionCalls(s, overloadData, classContext); - s << endl; + s << Qt::endl; - s << INDENT << "if (PyErr_Occurred() || !Shiboken::Object::setCppPointer(sbkSelf, Shiboken::SbkType< ::" << metaClass->qualifiedCppName() << " >(), cptr)) {" << endl; + s << INDENT << "if (PyErr_Occurred() || !Shiboken::Object::setCppPointer(sbkSelf, Shiboken::SbkType< ::" << metaClass->qualifiedCppName() << " >(), cptr)) {\n"; { Indentation indent(INDENT); - s << INDENT << "delete cptr;" << endl; - s << INDENT << returnStatement(m_currentErrorCode) << endl; + s << INDENT << "delete cptr;\n"; + if (overloadData.maxArgs() > 0) + s << INDENT << "Py_XDECREF(errInfo);\n"; + s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl; } - s << INDENT << '}' << endl; + s << INDENT << "}\n"; if (overloadData.maxArgs() > 0) { - s << INDENT << "if (!cptr) goto " << cpythonFunctionName(rfunc) << "_TypeError;" << endl; - s << endl; + s << INDENT << "if (!cptr) goto " << cpythonFunctionName(rfunc) << "_TypeError;\n"; + s << Qt::endl; } - s << INDENT << "Shiboken::Object::setValidCpp(sbkSelf, true);" << endl; + s << INDENT << "Shiboken::Object::setValidCpp(sbkSelf, true);\n"; // If the created C++ object has a C++ wrapper the ownership is assigned to Python // (first "1") and the flag indicating that the Python wrapper holds an C++ wrapper // is marked as true (the second "1"). Otherwise the default values apply: // Python owns it and C++ wrapper is false. if (shouldGenerateCppWrapper(overloads.constFirst()->ownerClass())) - s << INDENT << "Shiboken::Object::setHasCppWrapper(sbkSelf, true);" << endl; + s << INDENT << "Shiboken::Object::setHasCppWrapper(sbkSelf, true);\n"; // Need to check if a wrapper for same pointer is already registered // Caused by bug PYSIDE-217, where deleted objects' wrappers are not released - s << INDENT << "if (Shiboken::BindingManager::instance().hasWrapper(cptr)) {" << endl; + s << INDENT << "if (Shiboken::BindingManager::instance().hasWrapper(cptr)) {\n"; { Indentation indent(INDENT); - s << INDENT << "Shiboken::BindingManager::instance().releaseWrapper(Shiboken::BindingManager::instance().retrieveWrapper(cptr));" << endl; + s << INDENT << "Shiboken::BindingManager::instance().releaseWrapper(" + "Shiboken::BindingManager::instance().retrieveWrapper(cptr));\n"; } - s << INDENT << "}" << endl; - s << INDENT << "Shiboken::BindingManager::instance().registerWrapper(sbkSelf, cptr);" << endl; + s << INDENT << "}\n"; + s << INDENT << "Shiboken::BindingManager::instance().registerWrapper(sbkSelf, cptr);\n"; // Create metaObject and register signal/slot + bool errHandlerNeeded = overloadData.maxArgs() > 0; if (metaClass->isQObject() && usePySideExtensions()) { - s << endl << INDENT << "// QObject setup" << endl; - s << INDENT << "PySide::Signal::updateSourceObject(self);" << endl; - s << INDENT << "metaObject = cptr->metaObject(); // <- init python qt properties" << endl; - s << INDENT << "if (kwds && !PySide::fillQtProperties(self, metaObject, kwds, argNames, " << argNamesSet.count() << "))" << endl; - { - Indentation indentation(INDENT); - s << INDENT << returnStatement(m_currentErrorCode) << endl; - } + errHandlerNeeded = true; + s << Qt::endl << INDENT << "// QObject setup\n"; + s << INDENT << "PySide::Signal::updateSourceObject(self);\n"; + s << INDENT << "metaObject = cptr->metaObject(); // <- init python qt properties\n"; + s << INDENT << "if (errInfo && PyDict_Check(errInfo)) {\n" << indent(INDENT) + << INDENT << "if (!PySide::fillQtProperties(self, metaObject, errInfo))\n" << indent(INDENT) + << INDENT << "goto " << cpythonFunctionName(rfunc) << "_TypeError;\n" << outdent(INDENT) + << INDENT << "Py_DECREF(errInfo);\n" << outdent(INDENT) + << INDENT << "};\n"; } // Constructor code injections, position=end @@ -1793,35 +1966,36 @@ void CppGenerator::writeConstructorWrapper(QTextStream &s, const AbstractMetaFun } if (hasCodeInjectionsAtEnd) { // FIXME: C++ arguments are not available in code injection on constructor when position = end. - s << INDENT << "switch(overloadId) {" << endl; + s << INDENT << "switch (overloadId) {\n"; for (AbstractMetaFunction *func : overloads) { Indentation indent(INDENT); const CodeSnipList &injectedCodeSnips = func->injectedCodeSnips(); for (const CodeSnip &cs : injectedCodeSnips) { if (cs.position == TypeSystem::CodeSnipPositionEnd) { - s << INDENT << "case " << metaClass->functions().indexOf(func) << ':' << endl; - s << INDENT << '{' << endl; + s << INDENT << "case " << metaClass->functions().indexOf(func) << ':' << Qt::endl; + s << INDENT << "{\n"; { Indentation indent(INDENT); writeCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionEnd, TypeSystem::TargetLangCode, func); } - s << INDENT << '}' << endl; + s << INDENT << "}\n" + << INDENT << "break;\n"; break; } } } - s << '}' << endl; + s << "}\n"; } - s << endl; - s << endl << INDENT << "return 1;" << endl; - if (overloadData.maxArgs() > 0) + s << Qt::endl; + s << Qt::endl << INDENT << "return 1;\n"; + if (errHandlerNeeded) writeErrorSection(s, overloadData); - s << '}' << endl << endl; + s<< "}\n\n"; } void CppGenerator::writeMethodWrapper(QTextStream &s, const AbstractMetaFunctionList &overloads, - GeneratorContext &classContext) + const GeneratorContext &classContext) { OverloadData overloadData(overloads, this); const AbstractMetaFunction *rfunc = overloadData.referenceFunction(); @@ -1835,11 +2009,11 @@ void CppGenerator::writeMethodWrapper(QTextStream &s, const AbstractMetaFunction if (overloadData.hasArgumentWithDefaultValue() || rfunc->isCallOperator()) s << ", PyObject *kwds"; } - s << ')' << endl << '{' << endl; + s << ")\n{\n"; writeMethodWrapperPreamble(s, overloadData, classContext); - s << endl; + s << Qt::endl; /* * This code is intended for shift operations only: @@ -1855,45 +2029,49 @@ void CppGenerator::writeMethodWrapper(QTextStream &s, const AbstractMetaFunction && !rfunc->isInplaceOperator() && !rfunc->isCallOperator() && rfunc->isOperatorOverload(); + + QScopedPointer<Indentation> reverseIndent; + if (callExtendedReverseOperator) { QString revOpName = ShibokenGenerator::pythonOperatorFunctionName(rfunc).insert(2, QLatin1Char('r')); // For custom classes, operations like __radd__ and __rmul__ // will enter an infinite loop. if (rfunc->isBinaryOperator() && revOpName.contains(QLatin1String("shift"))) { - s << INDENT << "Shiboken::AutoDecRef attrName(Py_BuildValue(\"s\", \"" << revOpName << "\"));" << endl; - s << INDENT << "if (!isReverse" << endl; + s << INDENT << "Shiboken::AutoDecRef attrName(Py_BuildValue(\"s\", \"" << revOpName << "\"));\n"; + s << INDENT << "if (!isReverse\n"; { Indentation indent(INDENT); - s << INDENT << "&& Shiboken::Object::checkType(" << PYTHON_ARG << ")" << endl; - s << INDENT << "&& !PyObject_TypeCheck(" << PYTHON_ARG << ", self->ob_type)" << endl; - s << INDENT << "&& PyObject_HasAttr(" << PYTHON_ARG << ", attrName)) {" << endl; + s << INDENT << "&& Shiboken::Object::checkType(" << PYTHON_ARG << ")\n"; + s << INDENT << "&& !PyObject_TypeCheck(" << PYTHON_ARG << ", self->ob_type)\n"; + s << INDENT << "&& PyObject_HasAttr(" << PYTHON_ARG << ", attrName)) {\n"; // This PyObject_CallMethod call will emit lots of warnings like // "deprecated conversion from string constant to char *" during compilation // due to the method name argument being declared as "char *" instead of "const char *" // issue 6952 http://bugs.python.org/issue6952 - s << INDENT << "PyObject *revOpMethod = PyObject_GetAttr(" << PYTHON_ARG << ", attrName);" << endl; - s << INDENT << "if (revOpMethod && PyCallable_Check(revOpMethod)) {" << endl; + s << INDENT << "PyObject *revOpMethod = PyObject_GetAttr(" << PYTHON_ARG << ", attrName);\n"; + s << INDENT << "if (revOpMethod && PyCallable_Check(revOpMethod)) {\n"; { Indentation indent(INDENT); - s << INDENT << PYTHON_RETURN_VAR << " = PyObject_CallFunction(revOpMethod, const_cast<char *>(\"O\"), self);" << endl; + s << INDENT << PYTHON_RETURN_VAR << " = PyObject_CallFunction(revOpMethod, const_cast<char *>(\"O\"), self);\n"; s << INDENT << "if (PyErr_Occurred() && (PyErr_ExceptionMatches(PyExc_NotImplementedError)"; - s << " || PyErr_ExceptionMatches(PyExc_AttributeError))) {" << endl; + s << " || PyErr_ExceptionMatches(PyExc_AttributeError))) {\n"; { Indentation indent(INDENT); - s << INDENT << "PyErr_Clear();" << endl; - s << INDENT << "Py_XDECREF(" << PYTHON_RETURN_VAR << ");" << endl; - s << INDENT << PYTHON_RETURN_VAR << " = " << NULL_PTR << ';' << endl; + s << INDENT << "PyErr_Clear();\n"; + s << INDENT << "Py_XDECREF(" << PYTHON_RETURN_VAR << ");\n"; + s << INDENT << PYTHON_RETURN_VAR << " = " << NULL_PTR << ";\n"; } - s << INDENT << '}' << endl; + s << INDENT << "}\n"; } - s << INDENT << "}" << endl; - s << INDENT << "Py_XDECREF(revOpMethod);" << endl << endl; - } - s << INDENT << "}" << endl; - } - s << INDENT << "// Do not enter here if other object has implemented a reverse operator." << endl; - s << INDENT << "if (!" << PYTHON_RETURN_VAR << ") {" << endl << endl; + s << INDENT << "}\n"; + s << INDENT << "Py_XDECREF(revOpMethod);\n\n"; + } // + s << INDENT << "}\n\n"; + s << INDENT << "// Do not enter here if other object has implemented a reverse operator.\n"; + s << INDENT << "if (!" << PYTHON_RETURN_VAR << ") {\n"; + reverseIndent.reset(new Indentation(INDENT)); + } // binary shift operator } if (maxArgs > 0) @@ -1901,10 +2079,12 @@ void CppGenerator::writeMethodWrapper(QTextStream &s, const AbstractMetaFunction writeFunctionCalls(s, overloadData, classContext); - if (callExtendedReverseOperator) - s << endl << INDENT << "} // End of \"if (!" << PYTHON_RETURN_VAR << ")\"" << endl; + if (!reverseIndent.isNull()) { // binary shift operator + reverseIndent.reset(); + s << Qt::endl << INDENT << "} // End of \"if (!" << PYTHON_RETURN_VAR << ")\"\n"; + } - s << endl; + s << Qt::endl; writeFunctionReturnErrorCheckSection(s, hasReturnValue && !rfunc->isInplaceOperator()); @@ -1916,19 +2096,19 @@ void CppGenerator::writeMethodWrapper(QTextStream &s, const AbstractMetaFunction s << INDENT << "return " << PYTHON_RETURN_VAR << ";\n"; } } else { - s << INDENT << "Py_RETURN_NONE;" << endl; + s << INDENT << "Py_RETURN_NONE;\n"; } if (maxArgs > 0) writeErrorSection(s, overloadData); - s << '}' << endl << endl; + s<< "}\n\n"; } void CppGenerator::writeArgumentsInitializer(QTextStream &s, OverloadData &overloadData) { const AbstractMetaFunction *rfunc = overloadData.referenceFunction(); - s << "PyTuple_GET_SIZE(args);" << endl; + s << "PyTuple_GET_SIZE(args);\n"; writeUnusedVariableCast(s, QLatin1String("numArgs")); int minArgs = overloadData.minArgs(); @@ -1936,33 +2116,36 @@ void CppGenerator::writeArgumentsInitializer(QTextStream &s, OverloadData &overl s << INDENT << "PyObject *"; s << PYTHON_ARGS << "[] = {" - << QString(maxArgs, QLatin1Char('0')).split(QLatin1String(""), QString::SkipEmptyParts).join(QLatin1String(", ")) - << "};" << endl; - s << endl; + << QString(maxArgs, QLatin1Char('0')).split(QLatin1String(""), Qt::SkipEmptyParts).join(QLatin1String(", ")) + << "};\n"; + s << Qt::endl; if (overloadData.hasVarargs()) { maxArgs--; if (minArgs > maxArgs) minArgs = maxArgs; - s << INDENT << "PyObject *nonvarargs = PyTuple_GetSlice(args, 0, " << maxArgs << ");" << endl; - s << INDENT << "Shiboken::AutoDecRef auto_nonvarargs(nonvarargs);" << endl; - s << INDENT << PYTHON_ARGS << '[' << maxArgs << "] = PyTuple_GetSlice(args, " << maxArgs << ", numArgs);" << endl; - s << INDENT << "Shiboken::AutoDecRef auto_varargs(" << PYTHON_ARGS << "[" << maxArgs << "]);" << endl; - s << endl; + s << INDENT << "PyObject *nonvarargs = PyTuple_GetSlice(args, 0, " << maxArgs << ");\n"; + s << INDENT << "Shiboken::AutoDecRef auto_nonvarargs(nonvarargs);\n"; + s << INDENT << PYTHON_ARGS << '[' << maxArgs << "] = PyTuple_GetSlice(args, " << maxArgs << ", numArgs);\n"; + s << INDENT << "Shiboken::AutoDecRef auto_varargs(" << PYTHON_ARGS << "[" << maxArgs << "]);\n"; + s << Qt::endl; } bool usesNamedArguments = overloadData.hasArgumentWithDefaultValue(); - s << INDENT << "// invalid argument lengths" << endl; + s << INDENT << "// invalid argument lengths\n"; bool ownerClassIsQObject = rfunc->ownerClass() && rfunc->ownerClass()->isQObject() && rfunc->isConstructor(); if (usesNamedArguments) { if (!ownerClassIsQObject) { - s << INDENT << "if (numArgs" << (overloadData.hasArgumentWithDefaultValue() ? " + numNamedArgs" : "") << " > " << maxArgs << ") {" << endl; + s << INDENT << "if (numArgs > " << maxArgs << ") {\n"; { Indentation indent(INDENT); - s << INDENT << "PyErr_SetString(PyExc_TypeError, \"" << fullPythonFunctionName(rfunc) << "(): too many arguments\");" << endl; - s << INDENT << returnStatement(m_currentErrorCode) << endl; + s << INDENT << "static PyObject *const too_many = " + "Shiboken::String::createStaticString(\">\");\n"; + s << INDENT << "errInfo = too_many;\n"; + s << INDENT << "Py_INCREF(errInfo);\n"; + s << INDENT << "goto " << cpythonFunctionName(rfunc) << "_TypeError;\n"; } s << INDENT << '}'; } @@ -1971,11 +2154,14 @@ void CppGenerator::writeArgumentsInitializer(QTextStream &s, OverloadData &overl s << INDENT; else s << " else "; - s << "if (numArgs < " << minArgs << ") {" << endl; + s << "if (numArgs < " << minArgs << ") {\n"; { Indentation indent(INDENT); - s << INDENT << "PyErr_SetString(PyExc_TypeError, \"" << fullPythonFunctionName(rfunc) << "(): not enough arguments\");" << endl; - s << INDENT << returnStatement(m_currentErrorCode) << endl; + s << INDENT << "static PyObject *const too_few = " + "Shiboken::String::createStaticString(\"<\");\n"; + s << INDENT << "errInfo = too_few;\n"; + s << INDENT << "Py_INCREF(errInfo);\n"; + s << INDENT << "goto " << cpythonFunctionName(rfunc) << "_TypeError;\n"; } s << INDENT << '}'; } @@ -1989,11 +2175,11 @@ void CppGenerator::writeArgumentsInitializer(QTextStream &s, OverloadData &overl s << " else "; else s << INDENT; - s << "if (" << invArgsLen.join(QLatin1String(" || ")) << ")" << endl; + s << "if (" << invArgsLen.join(QLatin1String(" || ")) << ")\n"; Indentation indent(INDENT); s << INDENT << "goto " << cpythonFunctionName(rfunc) << "_TypeError;"; } - s << endl << endl; + s << Qt::endl << Qt::endl; QString funcName; if (rfunc->isOperatorOverload()) @@ -2009,24 +2195,18 @@ void CppGenerator::writeArgumentsInitializer(QTextStream &s, OverloadData &overl s << "PyArg_UnpackTuple(" << argsVar << ", \"" << funcName << "\", " << minArgs << ", " << maxArgs; for (int i = 0; i < maxArgs; i++) s << ", &(" << PYTHON_ARGS << '[' << i << "])"; - s << "))" << endl; + s << "))\n"; { Indentation indent(INDENT); - s << INDENT << returnStatement(m_currentErrorCode) << endl; + s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl; } - s << endl; + s << Qt::endl; } -void CppGenerator::writeCppSelfAssigment(QTextStream &s, const GeneratorContext &context, - const QString &className, bool cppSelfAsReference, - bool useWrapperClass) +void CppGenerator::writeCppSelfConversion(QTextStream &s, const GeneratorContext &context, + const QString &className, bool useWrapperClass) { static const QString pythonSelfVar = QLatin1String("self"); - if (cppSelfAsReference) - s << className << " &"; - s << CPP_SELF_VAR << " = "; - if (cppSelfAsReference) - s << " *"; if (useWrapperClass) s << "static_cast<" << className << " *>("; if (!context.forSmartPointer()) @@ -2038,49 +2218,58 @@ void CppGenerator::writeCppSelfAssigment(QTextStream &s, const GeneratorContext } void CppGenerator::writeCppSelfDefinition(QTextStream &s, - GeneratorContext &context, + const GeneratorContext &context, bool hasStaticOverload, bool cppSelfAsReference) { + Q_ASSERT(!(cppSelfAsReference && hasStaticOverload)); + const AbstractMetaClass *metaClass = context.metaClass(); - bool useWrapperClass = avoidProtectedHack() && metaClass->hasProtectedMembers(); + bool useWrapperClass = avoidProtectedHack() && metaClass->hasProtectedMembers() + && !metaClass->attributes().testFlag(AbstractMetaAttributes::FinalCppClass); + Q_ASSERT(!useWrapperClass || context.useWrapper()); QString className; if (!context.forSmartPointer()) { className = useWrapperClass - ? wrapperName(metaClass) + ? context.wrapperName() : (QLatin1String("::") + metaClass->qualifiedCppName()); } else { - className = context.preciseType()->cppSignature(); + className = context.smartPointerWrapperName(); } - if (!cppSelfAsReference) { - s << INDENT << className << " *" << CPP_SELF_VAR << " = nullptr;" << endl; - writeUnusedVariableCast(s, QLatin1String(CPP_SELF_VAR)); + writeInvalidPyObjectCheck(s, QLatin1String("self")); + + if (cppSelfAsReference) { + s << INDENT << "auto &" << CPP_SELF_VAR << " = *"; + writeCppSelfConversion(s, context, className, useWrapperClass); + s << ";\n"; + return; } - // Checks if the underlying C++ object is valid. - if (hasStaticOverload && !cppSelfAsReference) { - s << INDENT << "if (self) {" << endl; - { - Indentation indent(INDENT); - writeInvalidPyObjectCheck(s, QLatin1String("self")); - s << INDENT; - writeCppSelfAssigment(s, context, className, cppSelfAsReference, useWrapperClass); - s << ';' << endl; - } - s << INDENT << '}' << endl; + if (!hasStaticOverload) { + s << INDENT << "auto " << CPP_SELF_VAR << " = "; + writeCppSelfConversion(s, context, className, useWrapperClass); + s << ";\n"; + writeUnusedVariableCast(s, QLatin1String(CPP_SELF_VAR)); return; } - writeInvalidPyObjectCheck(s, QLatin1String("self")); - s << INDENT; - writeCppSelfAssigment(s, context, className, cppSelfAsReference, useWrapperClass); - s << ';' << endl; + s << INDENT << className << " *" << CPP_SELF_VAR << " = nullptr;\n"; + writeUnusedVariableCast(s, QLatin1String(CPP_SELF_VAR)); + + // Checks if the underlying C++ object is valid. + s << INDENT << "if (self)\n"; + { + Indentation indent(INDENT); + s << INDENT << CPP_SELF_VAR << " = "; + writeCppSelfConversion(s, context, className, useWrapperClass); + s << ";\n"; + } } void CppGenerator::writeCppSelfDefinition(QTextStream &s, const AbstractMetaFunction *func, - GeneratorContext &context, + const GeneratorContext &context, bool hasStaticOverload) { if (!func->ownerClass() || func->isConstructor()) @@ -2088,14 +2277,14 @@ void CppGenerator::writeCppSelfDefinition(QTextStream &s, if (func->isOperatorOverload() && func->isBinaryOperator()) { QString checkFunc = cpythonCheckFunction(func->ownerClass()->typeEntry()); - s << INDENT << "bool isReverse = " << checkFunc << PYTHON_ARG << ')' << endl; + s << INDENT << "bool isReverse = " << checkFunc << PYTHON_ARG << ")\n"; { Indentation indent1(INDENT, 4); - s << INDENT << "&& !" << checkFunc << "self);" << endl; + s << INDENT << "&& !" << checkFunc << "self);\n"; } - s << INDENT << "if (isReverse)" << endl; + s << INDENT << "if (isReverse)\n"; Indentation indent(INDENT); - s << INDENT << "std::swap(self, " << PYTHON_ARG << ");" << endl; + s << INDENT << "std::swap(self, " << PYTHON_ARG << ");\n"; } writeCppSelfDefinition(s, context, hasStaticOverload); @@ -2104,14 +2293,16 @@ void CppGenerator::writeCppSelfDefinition(QTextStream &s, void CppGenerator::writeErrorSection(QTextStream &s, OverloadData &overloadData) { const AbstractMetaFunction *rfunc = overloadData.referenceFunction(); - s << endl << INDENT << cpythonFunctionName(rfunc) << "_TypeError:" << endl; + s << Qt::endl << INDENT << cpythonFunctionName(rfunc) << "_TypeError:\n"; Indentation indentation(INDENT); - QString funcName = fullPythonFunctionName(rfunc); + QString funcName = fullPythonFunctionName(rfunc, true); QString argsVar = pythonFunctionWrapperUsesListOfArguments(overloadData) ? QLatin1String("args") : QLatin1String(PYTHON_ARG); - s << INDENT << "Shiboken::setErrorAboutWrongArguments(" << argsVar << ", \"" << funcName << "\");" << endl; - s << INDENT << "return " << m_currentErrorCode << ';' << endl; + s << INDENT << "Shiboken::setErrorAboutWrongArguments(" << argsVar + << ", fullName, errInfo);\n"; + s << INDENT << "Py_XDECREF(errInfo);\n"; + s << INDENT << "return " << m_currentErrorCode << ";\n"; } void CppGenerator::writeFunctionReturnErrorCheckSection(QTextStream &s, bool hasReturnValue) @@ -2119,21 +2310,21 @@ void CppGenerator::writeFunctionReturnErrorCheckSection(QTextStream &s, bool has s << INDENT << "if (PyErr_Occurred()"; if (hasReturnValue) s << " || !" << PYTHON_RETURN_VAR; - s << ") {" << endl; + s << ") {\n"; { Indentation indent(INDENT); if (hasReturnValue) - s << INDENT << "Py_XDECREF(" << PYTHON_RETURN_VAR << ");" << endl; - s << INDENT << returnStatement(m_currentErrorCode) << endl; + s << INDENT << "Py_XDECREF(" << PYTHON_RETURN_VAR << ");\n"; + s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl; } - s << INDENT << '}' << endl; + s << INDENT << "}\n"; } void CppGenerator::writeInvalidPyObjectCheck(QTextStream &s, const QString &pyObj) { - s << INDENT << "if (!Shiboken::Object::isValid(" << pyObj << "))" << endl; + s << INDENT << "if (!Shiboken::Object::isValid(" << pyObj << "))\n"; Indentation indent(INDENT); - s << INDENT << returnStatement(m_currentErrorCode) << endl; + s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl; } static QString pythonToCppConverterForArgumentName(const QString &argumentName) @@ -2187,6 +2378,7 @@ void CppGenerator::writeTypeCheck(QTextStream &s, const AbstractMetaType *argTyp static void checkTypeViability(const AbstractMetaFunction *func, const AbstractMetaType *type, int argIdx) { if (!type + || type->isVoid() || !type->typeEntry()->isPrimitive() || type->indirections() == 0 || (type->indirections() == 1 && type->typeUsagePattern() == AbstractMetaType::NativePointerAsArrayPattern) @@ -2198,7 +2390,8 @@ static void checkTypeViability(const AbstractMetaFunction *func, const AbstractM return; QString message; QTextStream str(&message); - str << "There's no user provided way (conversion rule, argument" + str << func->sourceLocation() + << "There's no user provided way (conversion rule, argument" " removal, custom code, etc) to handle the primitive "; if (argIdx == 0) str << "return type '" << type->cppSignature() << '\''; @@ -2336,7 +2529,7 @@ void CppGenerator::writePythonToCppTypeConversion(QTextStream &s, if (mayHaveImplicitConversion) { s << INDENT << typeName << ' ' << cppOutAux; writeMinimalConstructorExpression(s, type, defaultValue); - s << ';' << endl; + s << ";\n"; } else if (avoidProtectedHack() && type->typeEntry()->isEnum()) { const AbstractMetaEnum *metaEnum = findAbstractMetaEnum(type); if (metaEnum && metaEnum->isProtected()) { @@ -2350,8 +2543,17 @@ void CppGenerator::writePythonToCppTypeConversion(QTextStream &s, s << ' ' << cppOut; } else if (treatAsPointer || isPointerOrObjectType) { s << " *" << cppOut; - if (!defaultValue.isEmpty()) - s << " = " << defaultValue; + if (!defaultValue.isEmpty()) { + const bool needsConstCast = !isNullPtr(defaultValue) + && type->indirections() == 1 && type->isConstant() + && type->referenceType() == NoReference; + s << " = "; + if (needsConstCast) + s << "const_cast<" << typeName << " *>("; + s << defaultValue; + if (needsConstCast) + s << ')'; + } } else if (type->referenceType() == LValueReference && !typeEntry->isPrimitive() && isNotContainerEnumOrFlags) { s << " *" << cppOut << " = &" << cppOutAux; } else { @@ -2368,7 +2570,7 @@ void CppGenerator::writePythonToCppTypeConversion(QTextStream &s, writeMinimalConstructorExpression(s, type, defaultValue); } } - s << ';' << endl; + s << ";\n"; QString pythonToCppFunc = pythonToCppConverterForArgumentName(pyIn); @@ -2378,28 +2580,28 @@ void CppGenerator::writePythonToCppTypeConversion(QTextStream &s, QString pythonToCppCall = QString::fromLatin1("%1(%2, &%3)").arg(pythonToCppFunc, pyIn, cppOut); if (!mayHaveImplicitConversion) { - s << pythonToCppCall << ';' << endl; + s << pythonToCppCall << ";\n"; return; } if (!defaultValue.isEmpty()) - s << '{' << endl << INDENT; + s << "{\n" << INDENT; s << "if (Shiboken::Conversions::isImplicitConversion(reinterpret_cast<SbkObjectType *>(" - << cpythonTypeNameExt(type) << "), " << pythonToCppFunc << "))" << endl; + << cpythonTypeNameExt(type) << "), " << pythonToCppFunc << "))\n"; { Indentation indent(INDENT); - s << INDENT << pythonToCppFunc << '(' << pyIn << ", &" << cppOutAux << ");" << endl; + s << INDENT << pythonToCppFunc << '(' << pyIn << ", &" << cppOutAux << ");\n"; } - s << INDENT << "else" << endl; + s << INDENT << "else\n"; { Indentation indent(INDENT); - s << INDENT << pythonToCppCall << ';' << endl; + s << INDENT << pythonToCppCall << ";\n"; } if (!defaultValue.isEmpty()) s << INDENT << '}'; - s << endl; + s << Qt::endl; } static void addConversionRuleCodeSnippet(CodeSnipList &snippetList, QString &rule, @@ -2444,15 +2646,15 @@ void CppGenerator::writeConversionRule(QTextStream &s, const AbstractMetaFunctio void CppGenerator::writeNoneReturn(QTextStream &s, const AbstractMetaFunction *func, bool thereIsReturnValue) { - if (thereIsReturnValue && (!func->type() || func->argumentRemoved(0)) && !injectedCodeHasReturnValueAttribution(func)) { - s << INDENT << PYTHON_RETURN_VAR << " = Py_None;" << endl; - s << INDENT << "Py_INCREF(Py_None);" << endl; + if (thereIsReturnValue && (func->isVoid() || func->argumentRemoved(0)) && !injectedCodeHasReturnValueAttribution(func)) { + s << INDENT << PYTHON_RETURN_VAR << " = Py_None;\n"; + s << INDENT << "Py_INCREF(Py_None);\n"; } } void CppGenerator::writeOverloadedFunctionDecisor(QTextStream &s, const OverloadData &overloadData) { - s << INDENT << "// Overloaded function decisor" << endl; + s << INDENT << "// Overloaded function decisor\n"; const AbstractMetaFunction *rfunc = overloadData.referenceFunction(); const OverloadData::MetaFunctionList &functionOverloads = overloadData.overloadsWithoutRepetition(); for (int i = 0; i < functionOverloads.count(); i++) { @@ -2462,26 +2664,26 @@ void CppGenerator::writeOverloadedFunctionDecisor(QTextStream &s, const Overload s << "static "; if (const auto *decl = func->declaringClass()) s << decl->name() << "::"; - s << func->minimalSignature() << endl; + s << func->minimalSignature() << Qt::endl; } writeOverloadedFunctionDecisorEngine(s, &overloadData); - s << endl; + s << Qt::endl; // Ensure that the direct overload that called this reverse // is called. if (rfunc->isOperatorOverload() && !rfunc->isCallOperator()) { - s << INDENT << "if (isReverse && overloadId == -1) {" << endl; + s << INDENT << "if (isReverse && overloadId == -1) {\n"; { Indentation indent(INDENT); - s << INDENT << "PyErr_SetString(PyExc_NotImplementedError, \"reverse operator not implemented.\");" << endl; - s << INDENT << "return {};" << endl; + s << INDENT << "PyErr_SetString(PyExc_NotImplementedError, \"reverse operator not implemented.\");\n"; + s << INDENT << "return {};\n"; } - s << INDENT << "}" << endl << endl; + s << INDENT << "}\n\n"; } - s << INDENT << "// Function signature not found." << endl; - s << INDENT << "if (overloadId == -1) goto " << cpythonFunctionName(overloadData.referenceFunction()) << "_TypeError;" << endl; - s << endl; + s << INDENT << "// Function signature not found.\n"; + s << INDENT << "if (overloadId == -1) goto " << cpythonFunctionName(overloadData.referenceFunction()) << "_TypeError;\n"; + s << Qt::endl; } void CppGenerator::writeOverloadedFunctionDecisorEngine(QTextStream &s, const OverloadData *parentOverloadData) @@ -2512,7 +2714,7 @@ void CppGenerator::writeOverloadedFunctionDecisorEngine(QTextStream &s, const Ov // Functions without arguments are identified right away. if (maxArgs == 0) { s << INDENT << "overloadId = " << parentOverloadData->headOverloadData()->overloads().indexOf(referenceFunction); - s << "; // " << referenceFunction->minimalSignature() << endl; + s << "; // " << referenceFunction->minimalSignature() << Qt::endl; return; } @@ -2528,7 +2730,7 @@ void CppGenerator::writeOverloadedFunctionDecisorEngine(QTextStream &s, const Ov if (isLastArgument || (signatureFound && !hasDefaultCall)) { const AbstractMetaFunction *func = parentOverloadData->referenceFunction(); s << INDENT << "overloadId = " << parentOverloadData->headOverloadData()->overloads().indexOf(func); - s << "; // " << func->minimalSignature() << endl; + s << "; // " << func->minimalSignature() << Qt::endl; return; } } @@ -2542,7 +2744,7 @@ void CppGenerator::writeOverloadedFunctionDecisorEngine(QTextStream &s, const Ov if (hasDefaultCall) { isFirst = false; int numArgs = parentOverloadData->argPos() + 1; - s << INDENT << "if (numArgs == " << numArgs << ") {" << endl; + s << INDENT << "if (numArgs == " << numArgs << ") {\n"; { Indentation indent(INDENT); const AbstractMetaFunction *func = referenceFunction; @@ -2554,7 +2756,7 @@ void CppGenerator::writeOverloadedFunctionDecisorEngine(QTextStream &s, const Ov } } s << INDENT << "overloadId = " << parentOverloadData->headOverloadData()->overloads().indexOf(func); - s << "; // " << func->minimalSignature() << endl; + s << "; // " << func->minimalSignature() << Qt::endl; } s << INDENT << '}'; } @@ -2587,7 +2789,7 @@ void CppGenerator::writeOverloadedFunctionDecisorEngine(QTextStream &s, const Ov const AbstractMetaClass *ownerClass = func->ownerClass(); const ComplexTypeEntry *baseContainerType = ownerClass->typeEntry()->baseContainerType(); if (baseContainerType && baseContainerType == func->arguments().constFirst()->type()->typeEntry() && isCopyable(ownerClass)) { - tck << '!' << cpythonCheckFunction(ownerClass->typeEntry()) << pyArgName << ')' << endl; + tck << '!' << cpythonCheckFunction(ownerClass->typeEntry()) << pyArgName << ")\n"; Indentation indent(INDENT); tck << INDENT << "&& "; } @@ -2611,9 +2813,11 @@ void CppGenerator::writeOverloadedFunctionDecisorEngine(QTextStream &s, const Ov if (usePyArgs && signatureFound) { AbstractMetaArgumentList args = refFunc->arguments(); - int lastArgIsVarargs = (int) (args.size() > 1 && args.constLast()->type()->isVarargs()); - int numArgs = args.size() - OverloadData::numberOfRemovedArguments(refFunc) - lastArgIsVarargs; - typeChecks.prepend(QString::fromLatin1("numArgs %1 %2").arg(lastArgIsVarargs ? QLatin1String(">=") : QLatin1String("==")).arg(numArgs)); + const bool isVarargs = args.size() > 1 && args.constLast()->type()->isVarargs(); + int numArgs = args.size() - OverloadData::numberOfRemovedArguments(refFunc); + if (isVarargs) + --numArgs; + typeChecks.prepend(QString::fromLatin1("numArgs %1 %2").arg(isVarargs ? QLatin1String(">=") : QLatin1String("==")).arg(numArgs)); } else if (sequenceArgCount > 1) { typeChecks.prepend(QString::fromLatin1("numArgs >= %1").arg(startArg + sequenceArgCount)); } else if (refFunc->isOperatorOverload() && !refFunc->isCallOperator()) { @@ -2633,25 +2837,25 @@ void CppGenerator::writeOverloadedFunctionDecisorEngine(QTextStream &s, const Ov Indentation indent(INDENT); QString separator; QTextStream sep(&separator); - sep << endl << INDENT << "&& "; + sep << Qt::endl << INDENT << "&& "; s << typeChecks.join(separator); } - s << ") {" << endl; + s << ") {\n"; { Indentation indent(INDENT); writeOverloadedFunctionDecisorEngine(s, overloadData); } s << INDENT << "}"; } - s << endl; + s << Qt::endl; } void CppGenerator::writeFunctionCalls(QTextStream &s, const OverloadData &overloadData, - GeneratorContext &context) + const GeneratorContext &context) { const OverloadData::MetaFunctionList &overloads = overloadData.overloadsWithoutRepetition(); - s << INDENT << "// Call function/method" << endl; - s << INDENT << (overloads.count() > 1 ? "switch (overloadId) " : "") << '{' << endl; + s << INDENT << "// Call function/method\n"; + s << INDENT << (overloads.count() > 1 ? "switch (overloadId) " : "") << "{\n"; { Indentation indent(INDENT); if (overloads.count() == 1) { @@ -2659,8 +2863,8 @@ void CppGenerator::writeFunctionCalls(QTextStream &s, const OverloadData &overlo } else { for (int i = 0; i < overloads.count(); i++) { const AbstractMetaFunction *func = overloads.at(i); - s << INDENT << "case " << i << ": // " << func->signature() << endl; - s << INDENT << '{' << endl; + s << INDENT << "case " << i << ": // " << func->signature() << Qt::endl; + s << INDENT << "{\n"; { Indentation indent(INDENT); writeSingleFunctionCall(s, overloadData, func, context); @@ -2670,40 +2874,40 @@ void CppGenerator::writeFunctionCalls(QTextStream &s, const OverloadData &overlo s << cls->name() << '.'; s << func->signature() << " is deprecated\", 1);\n"; } - s << INDENT << "break;" << endl; + s << INDENT << "break;\n"; } - s << INDENT << '}' << endl; + s << INDENT << "}\n"; } } } - s << INDENT << '}' << endl; + s << INDENT << "}\n"; } void CppGenerator::writeSingleFunctionCall(QTextStream &s, const OverloadData &overloadData, const AbstractMetaFunction *func, - GeneratorContext &context) + const GeneratorContext &context) { if (func->isDeprecated()) { s << INDENT << "Shiboken::warning(PyExc_DeprecationWarning, 1, \"Function: '" << func->signature().replace(QLatin1String("::"), QLatin1String(".")) - << "' is marked as deprecated, please check the documentation for more information.\");" << endl; + << "' is marked as deprecated, please check the documentation for more information.\");\n"; } if (func->functionType() == AbstractMetaFunction::EmptyFunction) { s << INDENT << "PyErr_Format(PyExc_TypeError, \"%s is a private method.\", \"" << func->signature().replace(QLatin1String("::"), QLatin1String(".")) - << "\");" << endl; - s << INDENT << returnStatement(m_currentErrorCode) << endl; + << "\");\n"; + s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl; return; } bool usePyArgs = pythonFunctionWrapperUsesListOfArguments(overloadData); // Handle named arguments. - writeNamedArgumentResolution(s, func, usePyArgs); + writeNamedArgumentResolution(s, func, usePyArgs, overloadData); - bool injectCodeCallsFunc = injectedCodeCallsCppFunction(func); + bool injectCodeCallsFunc = injectedCodeCallsCppFunction(context, func); bool mayHaveUnunsedArguments = !func->isUserAdded() && func->hasInjectedCode() && injectCodeCallsFunc; int removedArgs = 0; for (int argIdx = 0; argIdx < func->arguments().count(); ++argIdx) { @@ -2714,7 +2918,7 @@ void CppGenerator::writeSingleFunctionCall(QTextStream &s, const QString cppArgRemoved = QLatin1String(CPP_ARG_REMOVED) + QString::number(argIdx); s << INDENT << getFullTypeName(arg->type()) << ' ' << cppArgRemoved; - s << " = " << guessScopeForDefaultValue(func, arg) << ';' << endl; + s << " = " << guessScopeForDefaultValue(func, arg) << ";\n"; writeUnusedVariableCast(s, cppArgRemoved); } else if (!injectCodeCallsFunc && !func->isUserAdded() && !hasConversionRule) { // When an argument is removed from a method signature and no other means of calling @@ -2737,18 +2941,15 @@ void CppGenerator::writeSingleFunctionCall(QTextStream &s, writeArgumentConversion(s, argType, argName, pyArgName, func->implementingClass(), defaultValue, func->isUserAdded()); } - s << endl; + s << Qt::endl; int numRemovedArgs = OverloadData::numberOfRemovedArguments(func); - s << INDENT << "if (!PyErr_Occurred()) {" << endl; - { - Indentation indentation(INDENT); - writeMethodCall(s, func, context, func->arguments().size() - numRemovedArgs); - if (!func->isConstructor()) - writeNoneReturn(s, func, overloadData.hasNonVoidReturnType()); - } - s << INDENT << '}' << endl; + s << INDENT << "if (!PyErr_Occurred()) {\n" << indent(INDENT); + writeMethodCall(s, func, context, func->arguments().size() - numRemovedArgs); + if (!func->isConstructor()) + writeNoneReturn(s, func, overloadData.hasNonVoidReturnType()); + s << outdent(INDENT) << INDENT << "}\n"; } QString CppGenerator::cppToPythonFunctionName(const QString &sourceTypeName, QString targetTypeName) @@ -2794,15 +2995,16 @@ void CppGenerator::writeCppToPythonFunction(QTextStream &s, const QString &code, processCodeSnip(prettyCode); s << "static PyObject *" << cppToPythonFunctionName(sourceTypeName, targetTypeName); - s << "(const void *cppIn) {" << endl; + s << "(const void *cppIn) {\n"; s << prettyCode; - s << '}' << endl; + s << "}\n"; } static void replaceCppToPythonVariables(QString &code, const QString &typeName) { - code.prepend(QLatin1String("auto &cppInRef = *reinterpret_cast<") - + typeName + QLatin1String(" *>(const_cast<void *>(cppIn));\n")); + const QString line = QLatin1String("auto &cppInRef = *reinterpret_cast<") + + typeName + QLatin1String(" *>(const_cast<void *>(cppIn));"); + CodeSnipAbstract::prependCode(&code, line); code.replace(QLatin1String("%INTYPE"), typeName); code.replace(QLatin1String("%OUTTYPE"), QLatin1String("PyObject *")); code.replace(QLatin1String("%in"), QLatin1String("cppInRef")); @@ -2846,9 +3048,9 @@ void CppGenerator::writePythonToCppFunction(QTextStream &s, const QString &code, formatCode(c, code, INDENT); processCodeSnip(prettyCode); s << "static void " << pythonToCppFunctionName(sourceTypeName, targetTypeName); - s << "(PyObject *pyIn, void *cppOut) {" << endl; + s << "(PyObject *pyIn, void *cppOut) {\n"; s << prettyCode; - s << '}' << endl; + s << "}\n"; } void CppGenerator::writeIsPythonConvertibleToCppFunction(QTextStream &s, @@ -2862,19 +3064,19 @@ void CppGenerator::writeIsPythonConvertibleToCppFunction(QTextStream &s, pythonToCppFuncName = pythonToCppFunctionName(sourceTypeName, targetTypeName); s << "static PythonToCppFunc " << convertibleToCppFunctionName(sourceTypeName, targetTypeName); - s << "(PyObject *pyIn) {" << endl; + s << "(PyObject *pyIn) {\n"; if (acceptNoneAsCppNull) { - s << INDENT << "if (pyIn == Py_None)" << endl; + s << INDENT << "if (pyIn == Py_None)\n"; Indentation indent(INDENT); - s << INDENT << "return Shiboken::Conversions::nonePythonToCppNullPtr;" << endl; + s << INDENT << "return Shiboken::Conversions::nonePythonToCppNullPtr;\n"; } - s << INDENT << "if (" << condition << ')' << endl; + s << INDENT << "if (" << condition << ")\n"; { Indentation indent(INDENT); - s << INDENT << "return " << pythonToCppFuncName << ';' << endl; + s << INDENT << "return " << pythonToCppFuncName << ";\n"; } - s << INDENT << "return {};" << endl; - s << '}' << endl; + s << INDENT << "return {};\n"; + s << "}\n"; } void CppGenerator::writePythonToCppConversionFunctions(QTextStream &s, @@ -2889,12 +3091,13 @@ void CppGenerator::writePythonToCppConversionFunctions(QTextStream &s, // Python to C++ conversion function. QString code; QTextStream c(&code); + Indentor nested; if (conversion.isEmpty()) - conversion = QLatin1Char('*') + cpythonWrapperCPtr(sourceType->typeEntry(), QLatin1String("pyIn")); + conversion = QLatin1Char('*') + cpythonWrapperCPtr(sourceType, QLatin1String("pyIn")); if (!preConversion.isEmpty()) - c << INDENT << preConversion << endl; - const QString fullTypeName = getFullTypeName(targetType->typeEntry()); - c << INDENT << "*reinterpret_cast<" << fullTypeName << " *>(cppOut) = " + c << nested << preConversion << Qt::endl; + const QString fullTypeName = targetType->isSmartPointer() ? targetType->cppSignature() : getFullTypeName(targetType->typeEntry()); + c << nested << "*reinterpret_cast<" << fullTypeName << " *>(cppOut) = " << fullTypeName << '(' << conversion << ");"; QString sourceTypeName = fixedCppTypeName(sourceType); QString targetTypeName = fixedCppTypeName(targetType); @@ -2904,7 +3107,7 @@ void CppGenerator::writePythonToCppConversionFunctions(QTextStream &s, if (typeCheck.isEmpty()) typeCheck = QString::fromLatin1("PyObject_TypeCheck(pyIn, %1)").arg(sourcePyType); writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, typeCheck); - s << endl; + s << Qt::endl; } void CppGenerator::writePythonToCppConversionFunctions(QTextStream &s, @@ -2976,11 +3179,10 @@ void CppGenerator::writePythonToCppConversionFunctions(QTextStream &s, const Abs } // Python to C++ conversion function. QString cppTypeName = getFullTypeNameWithoutModifiers(containerType); - QString code; - QTextStream c(&code); - c << INDENT << "auto &cppOutRef = *reinterpret_cast<" - << cppTypeName << " *>(cppOut);\n"; - code.append(toCppConversions.constFirst()->conversion()); + QString code = toCppConversions.constFirst()->conversion(); + const QString line = QLatin1String("auto &cppOutRef = *reinterpret_cast<") + + cppTypeName + QLatin1String(" *>(cppOut);"); + CodeSnipAbstract::prependCode(&code, line); for (int i = 0; i < containerType->instantiations().count(); ++i) { const AbstractMetaType *type = containerType->instantiations().at(i); QString typeName = getFullTypeName(type); @@ -3012,67 +3214,101 @@ void CppGenerator::writePythonToCppConversionFunctions(QTextStream &s, const Abs else typeCheck = QString::fromLatin1("%1pyIn)").arg(typeCheck); writeIsPythonConvertibleToCppFunction(s, typeName, typeName, typeCheck); - s << endl; + s << Qt::endl; } void CppGenerator::writeAddPythonToCppConversion(QTextStream &s, const QString &converterVar, const QString &pythonToCppFunc, const QString &isConvertibleFunc) { - s << INDENT << "Shiboken::Conversions::addPythonToCppValueConversion(" << converterVar << ',' << endl; + s << INDENT << "Shiboken::Conversions::addPythonToCppValueConversion(" << converterVar << ',' << Qt::endl; { Indentation indent(INDENT); - s << INDENT << pythonToCppFunc << ',' << endl; + s << INDENT << pythonToCppFunc << ',' << Qt::endl; s << INDENT << isConvertibleFunc; } - s << ");" << endl; + s << ");\n"; } -void CppGenerator::writeNamedArgumentResolution(QTextStream &s, const AbstractMetaFunction *func, bool usePyArgs) +void CppGenerator::writeNamedArgumentResolution(QTextStream &s, const AbstractMetaFunction *func, + bool usePyArgs, const OverloadData &overloadData) { const AbstractMetaArgumentList &args = OverloadData::getArgumentsWithDefaultValues(func); - if (args.isEmpty()) + if (args.isEmpty()) { + if (overloadData.hasArgumentWithDefaultValue()) { + s << INDENT << "if (kwds) {\n"; + { + Indentation indent(INDENT); + s << INDENT << "errInfo = kwds;\n"; + s << INDENT << "Py_INCREF(errInfo);\n"; + s << INDENT << "goto " << cpythonFunctionName(func) << "_TypeError;\n"; + } + s << INDENT << "}\n"; + } return; + } - QString pyErrString(QLatin1String("PyErr_SetString(PyExc_TypeError, \"") + fullPythonFunctionName(func) - + QLatin1String("(): got multiple values for keyword argument '%1'.\");")); - - s << INDENT << "if (kwds) {" << endl; + s << INDENT << "if (kwds) {\n"; { Indentation indent(INDENT); - s << INDENT << "PyObject *keyName = nullptr;" << endl; - s << INDENT << "PyObject *value = nullptr;" << endl; + s << INDENT << "PyObject *value{};\n"; + s << INDENT << "PyObject *kwds_dup = PyDict_Copy(kwds);\n"; for (const AbstractMetaArgument *arg : args) { - int pyArgIndex = arg->argumentIndex() - OverloadData::numberOfRemovedArguments(func, arg->argumentIndex()); + const int pyArgIndex = arg->argumentIndex() + - OverloadData::numberOfRemovedArguments(func, arg->argumentIndex()); QString pyArgName = usePyArgs ? pythonArgsAt(pyArgIndex) : QLatin1String(PYTHON_ARG); - s << INDENT << "keyName = Py_BuildValue(\"s\",\"" << arg->name() << "\");" << endl; - s << INDENT << "if (PyDict_Contains(kwds, keyName)) {" << endl; + QString pyKeyName = QLatin1String("key_") + arg->name(); + s << INDENT << "static PyObject *const " << pyKeyName + << " = Shiboken::String::createStaticString(\"" << arg->name() << "\");\n"; + s << INDENT << "if (PyDict_Contains(kwds, " << pyKeyName << ")) {\n"; { Indentation indent(INDENT); - s << INDENT << "value = PyDict_GetItem(kwds, keyName);" << endl; - s << INDENT << "if (value && " << pyArgName << ") {" << endl; + s << INDENT << "value = PyDict_GetItem(kwds, " << pyKeyName << ");\n"; + s << INDENT << "if (value && " << pyArgName << ") {\n"; { Indentation indent(INDENT); - s << INDENT << pyErrString.arg(arg->name()) << endl; - s << INDENT << returnStatement(m_currentErrorCode) << endl; + s << INDENT << "errInfo = " << pyKeyName << ";\n"; + s << INDENT << "Py_INCREF(errInfo);\n"; + s << INDENT << "goto " << cpythonFunctionName(func) << "_TypeError;\n"; } - s << INDENT << '}' << endl; - s << INDENT << "if (value) {" << endl; + s << INDENT << "}\n"; + s << INDENT << "if (value) {\n"; { Indentation indent(INDENT); - s << INDENT << pyArgName << " = value;" << endl; + s << INDENT << pyArgName << " = value;\n"; s << INDENT << "if (!"; - writeTypeCheck(s, arg->type(), pyArgName, isNumber(arg->type()->typeEntry()), func->typeReplaced(arg->argumentIndex() + 1)); - s << ')' << endl; + writeTypeCheck(s, arg->type(), pyArgName, isNumber(arg->type()->typeEntry()), + func->typeReplaced(arg->argumentIndex() + 1)); + s << ")\n"; { Indentation indent(INDENT); - s << INDENT << "goto " << cpythonFunctionName(func) << "_TypeError;" << endl; + s << INDENT << "goto " << cpythonFunctionName(func) << "_TypeError;\n"; } } - s << INDENT << '}' << endl; + s << INDENT << "}\n"; + s << INDENT << "PyDict_DelItem(kwds_dup, " << pyKeyName << ");\n"; } - s << INDENT << '}' << endl; + s << INDENT << "}\n"; } + // PYSIDE-1305: Handle keyword args correctly. + // Normal functions handle their parameters immediately. + // For constructors that are QObject, we need to delay that + // until extra keyword signals and properties are handled. + s << INDENT << "if (PyDict_Size(kwds_dup) > 0) {\n"; + { + Indentation indent(INDENT); + s << INDENT << "errInfo = kwds_dup;\n"; + if (!(func->isConstructor() && func->ownerClass()->isQObject())) + s << INDENT << "goto " << cpythonFunctionName(func) << "_TypeError;\n"; + else + s << INDENT << "// fall through to handle extra keyword signals and properties\n"; + } + s << INDENT << "} else {\n"; + { + Indentation indent(INDENT); + s << INDENT << "Py_DECREF(kwds_dup);\n"; + } + s << INDENT << "}\n"; } - s << INDENT << '}' << endl; + s << INDENT << "}\n"; } QString CppGenerator::argumentNameFromIndex(const AbstractMetaFunction *func, int argIndex, const AbstractMetaClass **wrappedClass) @@ -3085,7 +3321,7 @@ QString CppGenerator::argumentNameFromIndex(const AbstractMetaFunction *func, in } else if (argIndex == 0) { AbstractMetaType *funcType = func->type(); AbstractMetaType *returnType = getTypeWithoutContainer(funcType); - if (returnType) { + if (!returnType->isVoid()) { pyArgName = QLatin1String(PYTHON_RETURN_VAR); *wrappedClass = AbstractMetaClass::findClass(classes(), returnType->typeEntry()); } else { @@ -3125,14 +3361,17 @@ static QStringList defaultExceptionHandling() } void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *func, - GeneratorContext &context, int maxArgs) + const GeneratorContext &context, int maxArgs) { - s << INDENT << "// " << func->minimalSignature() << (func->isReverseOperator() ? " [reverse operator]": "") << endl; + s << INDENT << "// " << func->minimalSignature() << (func->isReverseOperator() ? " [reverse operator]": "") << Qt::endl; if (func->isConstructor()) { const CodeSnipList &snips = func->injectedCodeSnips(); for (const CodeSnip &cs : snips) { if (cs.position == TypeSystem::CodeSnipPositionEnd) { - s << INDENT << "overloadId = " << func->ownerClass()->functions().indexOf(const_cast<AbstractMetaFunction *const>(func)) << ';' << endl; + auto klass = func->ownerClass(); + s << INDENT << "overloadId = " + << klass->functions().indexOf(const_cast<AbstractMetaFunction *>(func)) + << ";\n"; break; } } @@ -3143,8 +3382,8 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f { Indentation indent(INDENT); s << INDENT << "PyErr_SetString(PyExc_NotImplementedError, \"pure virtual method '"; - s << func->ownerClass()->name() << '.' << func->name() << "()' not implemented.\");" << endl; - s << INDENT << returnStatement(m_currentErrorCode) << endl; + s << func->ownerClass()->name() << '.' << func->name() << "()' not implemented.\");\n"; + s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl; } s << INDENT << "}\n"; } @@ -3171,7 +3410,6 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f } writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::TargetLangCode, func, lastArg); - s << endl; } writeConversionRule(s, func, TypeSystem::NativeCode); @@ -3256,14 +3494,14 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f std::swap(firstArg, secondArg); QString op = func->originalName(); - op = op.right(op.size() - (sizeof("operator")/sizeof(char)-1)); + op.remove(0, int(std::strlen("operator"))); if (func->isBinaryOperator()) { if (func->isReverseOperator()) std::swap(firstArg, secondArg); if (((op == QLatin1String("++")) || (op == QLatin1String("--"))) && !func->isReverseOperator()) { - s << endl << INDENT << "for(int i=0; i < " << secondArg << "; i++, " << firstArg << op << ");" << endl; + s << Qt::endl << INDENT << "for (int i=0; i < " << secondArg << "; i++, " << firstArg << op << ");\n"; mc << firstArg; } else { mc << firstArg << ' ' << op << ' ' << secondArg; @@ -3271,35 +3509,38 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f } else { mc << op << ' ' << secondArg; } - } else if (!injectedCodeCallsCppFunction(func)) { + } else if (!injectedCodeCallsCppFunction(context, func)) { if (func->isConstructor()) { isCtor = true; - QString className = wrapperName(func->ownerClass()); + const auto owner = func->ownerClass(); + Q_ASSERT(owner == context.metaClass()); + QString className = context.useWrapper() + ? context.wrapperName() : owner->qualifiedCppName(); if (func->functionType() == AbstractMetaFunction::CopyConstructorFunction && maxArgs == 1) { mc << "new ::" << className << "(*" << CPP_ARG0 << ')'; } else { QString ctorCall = className + QLatin1Char('(') + userArgs.join(QLatin1String(", ")) + QLatin1Char(')'); - if (usePySideExtensions() && func->ownerClass()->isQObject()) { - s << INDENT << "void *addr = PySide::nextQObjectMemoryAddr();" << endl; - uva << "if (addr) {" << endl; + if (usePySideExtensions() && owner->isQObject()) { + s << INDENT << "void *addr = PySide::nextQObjectMemoryAddr();\n"; + uva << "if (addr) {\n"; { Indentation indent(INDENT); uva << INDENT << "cptr = " << "new (addr) ::" - << ctorCall << ';' << endl + << ctorCall << ";\n" << INDENT << "PySide::setNextQObjectMemoryAddr(0);" - << endl; + << Qt::endl; } - uva << INDENT << "} else {" << endl; + uva << INDENT << "} else {\n"; { Indentation indent(INDENT); uva << INDENT << "cptr = " << "new ::" - << ctorCall << ';' << endl; + << ctorCall << ";\n"; } - uva << INDENT << "}" << endl; + uva << INDENT << "}\n"; } else { mc << "new ::" << ctorCall; } @@ -3322,14 +3563,16 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f + QLatin1String(" *>(") + QLatin1String(CPP_SELF_VAR) + QLatin1Char(')'); if (func->isConstant()) { if (avoidProtectedHack()) { + auto ownerClass = func->ownerClass(); mc << "const_cast<const ::"; - if (func->ownerClass()->hasProtectedMembers()) { + if (ownerClass->hasProtectedMembers() + && !ownerClass->attributes().testFlag(AbstractMetaAttributes::FinalCppClass)) { // PYSIDE-500: Need a special wrapper cast when inherited - const QString selfWrapCast = func->ownerClass() == func->implementingClass() + const QString selfWrapCast = ownerClass == func->implementingClass() ? QLatin1String(CPP_SELF_VAR) - : QLatin1String("reinterpret_cast<") + wrapperName(func->ownerClass()) + : QLatin1String("reinterpret_cast<") + wrapperName(ownerClass) + QLatin1String(" *>(") + QLatin1String(CPP_SELF_VAR) + QLatin1Char(')'); - mc << wrapperName(func->ownerClass()); + mc << wrapperName(ownerClass); mc << " *>(" << selfWrapCast << ")->"; } else { @@ -3372,18 +3615,19 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f if (!avoidProtectedHack() || !func->isProtected()) { QString virtualCall(methodCall); QString normalCall(methodCall); - virtualCall = virtualCall.replace(QLatin1String("%CLASS_NAME"), - methodCallClassName); + virtualCall.replace(QLatin1String("%CLASS_NAME"), + methodCallClassName); normalCall.remove(QLatin1String("::%CLASS_NAME::")); methodCall.clear(); - mc << "Shiboken::Object::hasCppWrapper(reinterpret_cast<SbkObject *>(self)) ? "; - mc << virtualCall << " : " << normalCall; + mc << "Shiboken::Object::hasCppWrapper(reinterpret_cast<SbkObject *>(self))\n" + << INDENT << " ? " << virtualCall << '\n' + << INDENT << " : " << normalCall; } } } } - if (!injectedCodeCallsCppFunction(func)) { + if (!injectedCodeCallsCppFunction(context, func)) { const bool allowThread = func->allowThread(); const bool generateExceptionHandling = func->generateExceptionHandling(); if (generateExceptionHandling) { @@ -3394,13 +3638,13 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f << INDENT << "threadSaver.save();\n"; } } else if (allowThread) { - s << INDENT << BEGIN_ALLOW_THREADS << endl; + s << INDENT << BEGIN_ALLOW_THREADS << Qt::endl; } s << INDENT; if (isCtor) { s << (useVAddr.isEmpty() ? - QString::fromLatin1("cptr = %1;").arg(methodCall) : useVAddr) << endl; - } else if (func->type() && !func->isInplaceOperator()) { + QString::fromLatin1("cptr = %1;").arg(methodCall) : useVAddr) << Qt::endl; + } else if (!func->isVoid() && !func->isInplaceOperator()) { bool writeReturnType = true; if (avoidProtectedHack()) { const AbstractMetaEnum *metaEnum = findAbstractMetaEnum(func->type()); @@ -3425,9 +3669,9 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f } } s << " " << CPP_RETURN_VAR << " = "; - s << methodCall << ';' << endl; + s << methodCall << ";\n"; } else { - s << methodCall << ';' << endl; + s << methodCall << ";\n"; } if (allowThread) { @@ -3438,7 +3682,7 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f // Convert result if (!func->conversionRule(TypeSystem::TargetLangCode, 0).isEmpty()) { writeConversionRule(s, func, TypeSystem::TargetLangCode, QLatin1String(PYTHON_RETURN_VAR)); - } else if (!isCtor && !func->isInplaceOperator() && func->type() + } else if (!isCtor && !func->isInplaceOperator() && !func->isVoid() && !injectedCodeHasReturnValueAttribution(func, TypeSystem::TargetLangCode)) { s << INDENT << PYTHON_RETURN_VAR << " = "; if (isObjectTypeUsedAsValueType(func->type())) { @@ -3447,7 +3691,7 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f } else { writeToPythonConversion(s, func->type(), func->ownerClass(), QLatin1String(CPP_RETURN_VAR)); } - s << ';' << endl; + s << ";\n"; } if (generateExceptionHandling) { // "catch" code @@ -3459,10 +3703,8 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f } } - if (func->hasInjectedCode() && !func->isConstructor()) { - s << endl; + if (func->hasInjectedCode() && !func->isConstructor()) writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::TargetLangCode, func, lastArg); - } bool hasReturnPolicy = false; @@ -3485,12 +3727,12 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f hasReturnPolicy = true; if (!ownership_mods.isEmpty()) { - s << endl << INDENT << "// Ownership transferences." << endl; + s << Qt::endl << INDENT << "// Ownership transferences.\n"; for (const ArgumentModification &arg_mod : qAsConst(ownership_mods)) { const AbstractMetaClass *wrappedClass = nullptr; QString pyArgName = argumentNameFromIndex(func, arg_mod.index, &wrappedClass); if (!wrappedClass) { - s << "#error Invalid ownership modification for argument " << arg_mod.index << '(' << pyArgName << ')' << endl << endl; + s << "#error Invalid ownership modification for argument " << arg_mod.index << '(' << pyArgName << ")\n" << Qt::endl; break; } @@ -3513,7 +3755,7 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f } else { s << "invalidate(" << pyArgName << ");"; } - s << endl; + s << Qt::endl; } } else if (!refcount_mods.isEmpty()) { @@ -3533,7 +3775,7 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f } else { pyArgName = argumentNameFromIndex(func, arg_mod.index, &wrappedClass); if (pyArgName.isEmpty()) { - s << "#error Invalid reference count modification for argument " << arg_mod.index << endl << endl; + s << "#error Invalid reference count modification for argument " << arg_mod.index << Qt::endl << Qt::endl; break; } } @@ -3550,7 +3792,7 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f s << varName << "\", " << pyArgName << (refCount.action == ReferenceCount::Add ? ", true" : "") - << ");" << endl; + << ");\n"; if (arg_mod.index == 0) hasReturnPolicy = true; @@ -3590,29 +3832,29 @@ void CppGenerator::writeMultipleInheritanceInitializerFunction(QTextStream &s, c s << "static int mi_offsets[] = { "; for (int i = 0; i < ancestors.size(); i++) s << "-1, "; - s << "-1 };" << endl; - s << "int *" << endl; - s << multipleInheritanceInitializerFunctionName(metaClass) << "(const void *cptr)" << endl; - s << '{' << endl; - s << INDENT << "if (mi_offsets[0] == -1) {" << endl; + s << "-1 };\n"; + s << "int *\n"; + s << multipleInheritanceInitializerFunctionName(metaClass) << "(const void *cptr)\n"; + s << "{\n"; + s << INDENT << "if (mi_offsets[0] == -1) {\n"; { Indentation indent(INDENT); - s << INDENT << "std::set<int> offsets;" << endl; - s << INDENT << "const auto *class_ptr = reinterpret_cast<const " << className << " *>(cptr);" << endl; - s << INDENT << "const auto base = reinterpret_cast<uintptr_t>(class_ptr);" << endl; + s << INDENT << "std::set<int> offsets;\n"; + s << INDENT << "const auto *class_ptr = reinterpret_cast<const " << className << " *>(cptr);\n"; + s << INDENT << "const auto base = reinterpret_cast<uintptr_t>(class_ptr);\n"; for (const QString &ancestor : ancestors) - s << INDENT << "offsets.insert(int(" << ancestor << "));" << endl; + s << INDENT << "offsets.insert(int(" << ancestor << "));\n"; - s << endl; - s << INDENT << "offsets.erase(0);" << endl; - s << endl; + s << Qt::endl; + s << INDENT << "offsets.erase(0);\n"; + s << Qt::endl; s << INDENT << "std::copy(offsets.cbegin(), offsets.cend(), mi_offsets);\n"; } - s << INDENT << '}' << endl; - s << INDENT << "return mi_offsets;" << endl; - s << '}' << endl; + s << INDENT << "}\n"; + s << INDENT << "return mi_offsets;\n"; + s << "}\n"; } void CppGenerator::writeSpecialCastFunction(QTextStream &s, const AbstractMetaClass *metaClass) @@ -3637,7 +3879,7 @@ void CppGenerator::writePrimitiveConverterInitialization(QTextStream &s, const C { const TypeEntry *type = customConversion->ownerType(); QString converter = converterObject(type); - s << INDENT << "// Register converter for type '" << type->qualifiedTargetLangName() << "'." << endl; + s << INDENT << "// Register converter for type '" << type->qualifiedTargetLangName() << "'.\n"; s << INDENT << converter << " = Shiboken::Conversions::createConverter("; if (type->targetLangApiName() == type->name()) s << '0'; @@ -3646,8 +3888,8 @@ void CppGenerator::writePrimitiveConverterInitialization(QTextStream &s, const C else s << '&' << type->targetLangApiName() << "_Type"; QString typeName = fixedCppTypeName(type); - s << ", " << cppToPythonFunctionName(typeName, typeName) << ");" << endl; - s << INDENT << "Shiboken::Conversions::registerConverterName(" << converter << ", \"" << type->qualifiedCppName() << "\");" << endl; + s << ", " << cppToPythonFunctionName(typeName, typeName) << ");\n"; + s << INDENT << "Shiboken::Conversions::registerConverterName(" << converter << ", \"" << type->qualifiedCppName() << "\");\n"; writeCustomConverterRegister(s, customConversion, converter); } @@ -3669,15 +3911,15 @@ void CppGenerator::writeEnumConverterInitialization(QTextStream &s, const TypeEn if (enumType->isFlags()) flags = static_cast<const FlagsTypeEntry *>(enumType); - s << INDENT << "// Register converter for " << enumFlagName << " '" << enumType->qualifiedCppName() << "'." << endl; - s << INDENT << '{' << endl; + s << INDENT << "// Register converter for " << enumFlagName << " '" << enumType->qualifiedCppName() << "'.\n"; + s << INDENT << "{\n"; { Indentation indent(INDENT); QString typeName = fixedCppTypeName(enumType); - s << INDENT << "SbkConverter *converter = Shiboken::Conversions::createConverter(" << enumPythonType << ',' << endl; + s << INDENT << "SbkConverter *converter = Shiboken::Conversions::createConverter(" << enumPythonType << ',' << Qt::endl; { Indentation indent(INDENT); - s << INDENT << cppToPythonFunctionName(typeName, typeName) << ");" << endl; + s << INDENT << cppToPythonFunctionName(typeName, typeName) << ");\n"; } if (flags) { @@ -3697,7 +3939,7 @@ void CppGenerator::writeEnumConverterInitialization(QTextStream &s, const TypeEn writeAddPythonToCppConversion(s, QLatin1String("converter"), toCpp, isConv); } - s << INDENT << "Shiboken::Enum::setTypeConverter(" << enumPythonType << ", converter);" << endl; + s << INDENT << "Shiboken::Enum::setTypeConverter(" << enumPythonType << ", converter);\n"; QString signature = enumType->qualifiedCppName(); // Replace "QFlags<Class::Option>" by "Class::Options" @@ -3723,7 +3965,7 @@ void CppGenerator::writeEnumConverterInitialization(QTextStream &s, const TypeEn break; } } - s << INDENT << '}' << endl; + s << INDENT << "}\n"; if (!flags) writeEnumConverterInitialization(s, static_cast<const EnumTypeEntry *>(enumType)->flags()); @@ -3732,7 +3974,7 @@ void CppGenerator::writeEnumConverterInitialization(QTextStream &s, const TypeEn void CppGenerator::writeContainerConverterInitialization(QTextStream &s, const AbstractMetaType *type) { QByteArray cppSignature = QMetaObject::normalizedSignature(type->cppSignature().toUtf8()); - s << INDENT << "// Register converter for type '" << cppSignature << "'." << endl; + s << INDENT << "// Register converter for type '" << cppSignature << "'.\n"; QString converter = converterObject(type); s << INDENT << converter << " = Shiboken::Conversions::createConverter("; if (type->typeEntry()->targetLangApiName() == QLatin1String("PyObject")) { @@ -3744,22 +3986,60 @@ void CppGenerator::writeContainerConverterInitialization(QTextStream &s, const A s << '&' << baseName << "_Type"; } QString typeName = fixedCppTypeName(type); - s << ", " << cppToPythonFunctionName(typeName, typeName) << ");" << endl; + s << ", " << cppToPythonFunctionName(typeName, typeName) << ");\n"; QString toCpp = pythonToCppFunctionName(typeName, typeName); QString isConv = convertibleToCppFunctionName(typeName, typeName); - s << INDENT << "Shiboken::Conversions::registerConverterName(" << converter << ", \"" << cppSignature << "\");" << endl; + s << INDENT << "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 << INDENT << "Shiboken::Conversions::registerConverterName(" << converter << ", \"" << cppSignature << "\");" << endl; + s << INDENT << "Shiboken::Conversions::registerConverterName(" << converter << ", \"" << cppSignature << "\");\n"; } writeAddPythonToCppConversion(s, converterObject(type), toCpp, isConv); } +void CppGenerator::writeSmartPointerConverterInitialization(QTextStream &s, const AbstractMetaType *type) +{ + const QByteArray cppSignature = type->cppSignature().toUtf8(); + auto writeConversionRegister = [this, &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); + }; + + auto klass = AbstractMetaClass::findClass(classes(), type->instantiations().at(0)->typeEntry()); + if (!klass) + return; + + const auto classes = getBaseClasses(klass); + if (classes.isEmpty()) + return; + + s << INDENT << "// Register SmartPointer converter for type '" << cppSignature << "'." << Qt::endl; + s << INDENT << "///////////////////////////////////////////////////////////////////////////////////////"<< Qt::endl; + s << Qt::endl; + + for (auto k : classes) { + if (auto smartTargetType = findSmartPointerInstantiation(k->typeEntry())) + { + s << INDENT << "// Convert to SmartPointer derived class: [" << smartTargetType->cppSignature() << "]" << Qt::endl; + const QString converter = QLatin1String("Shiboken::Conversions::getConverter(\"%1\")").arg(smartTargetType->cppSignature()); + writeConversionRegister(type, fixedCppTypeName(smartTargetType), converter); + } else { + s << INDENT << "// Class not found:" << type->instantiations().at(0)->cppSignature(); + } + } + + s << INDENT << "///////////////////////////////////////////////////////////////////////////////////////"<< Qt::endl << Qt::endl; +} + void CppGenerator::writeExtendedConverterInitialization(QTextStream &s, const TypeEntry *externalType, const QVector<const AbstractMetaClass *>& conversions) { - s << INDENT << "// Extended implicit conversions for " << externalType->qualifiedTargetLangName() << '.' << endl; + s << INDENT << "// Extended implicit conversions for " << externalType->qualifiedTargetLangName() << '.' << Qt::endl; for (const AbstractMetaClass *sourceClass : conversions) { const QString converterVar = QLatin1String("reinterpret_cast<SbkObjectType *>(") + cppApiVariableName(externalType->targetLangPackage()) + QLatin1Char('[') @@ -3813,6 +4093,13 @@ bool CppGenerator::shouldGenerateGetSetList(const AbstractMetaClass *metaClass) if (!f->isStatic()) return true; } + // Generate all user-added properties unless Pyside extensions are used, + // in which only the explicitly specified ones are generated (rest is handled + // in libpyside). + return usePySideExtensions() + ? std::any_of(metaClass->propertySpecs().cbegin(), metaClass->propertySpecs().cend(), + [] (const QPropertySpec *s) { return s->generateGetSetDef(); }) + : !metaClass->propertySpecs().isEmpty(); return false; } @@ -3841,7 +4128,7 @@ QTextStream &operator<<(QTextStream &str, const pyTypeSlotEntry &e) void CppGenerator::writeClassDefinition(QTextStream &s, const AbstractMetaClass *metaClass, - GeneratorContext &classContext) + const GeneratorContext &classContext) { QString tp_flags; QString tp_init; @@ -3871,14 +4158,11 @@ void CppGenerator::writeClassDefinition(QTextStream &s, if (metaClass->isNamespace() || metaClass->hasPrivateDestructor()) { tp_dealloc = metaClass->hasPrivateDestructor() ? QLatin1String("SbkDeallocWrapperWithPrivateDtor") : - QLatin1String("object_dealloc /* PYSIDE-832: Prevent replacement of \"0\" with subtype_dealloc. */"); + QLatin1String("Sbk_object_dealloc /* PYSIDE-832: Prevent replacement of \"0\" with subtype_dealloc. */"); tp_init.clear(); } else { - QString deallocClassName; - if (shouldGenerateCppWrapper(metaClass)) - deallocClassName = wrapperName(metaClass); - else - deallocClassName = cppClassName; + QString deallocClassName = classContext.useWrapper() + ? classContext.wrapperName() : cppClassName; if (isQApp) tp_dealloc = QLatin1String("&SbkDeallocQAppWrapper"); else @@ -3887,17 +4171,11 @@ void CppGenerator::writeClassDefinition(QTextStream &s, tp_init = cpythonFunctionName(ctors.constFirst()); } - QString tp_getattro; - QString tp_setattro; - if (usePySideExtensions() && (metaClass->qualifiedCppName() == QLatin1String("QObject"))) { - tp_getattro = cpythonGetattroFunctionName(metaClass); - tp_setattro = cpythonSetattroFunctionName(metaClass); - } else { - if (classNeedsGetattroFunction(metaClass)) - tp_getattro = cpythonGetattroFunctionName(metaClass); - if (classNeedsSetattroFunction(metaClass)) - tp_setattro = cpythonSetattroFunctionName(metaClass); - } + const AttroCheck attroCheck = checkAttroFunctionNeeds(metaClass); + const QString tp_getattro = (attroCheck & AttroCheckFlag::GetattroMask) != 0 + ? cpythonGetattroFunctionName(metaClass) : QString(); + const QString tp_setattro = (attroCheck & AttroCheckFlag::SetattroMask) != 0 + ? cpythonSetattroFunctionName(metaClass) : QString(); if (metaClass->hasPrivateDestructor() || onlyPrivCtor) { // tp_flags = QLatin1String("Py_TPFLAGS_DEFAULT|Py_TPFLAGS_CHECKTYPES"); @@ -3915,15 +4193,14 @@ void CppGenerator::writeClassDefinition(QTextStream &s, tp_new = QLatin1String("SbkDummyNew /* PYSIDE-595: Prevent replacement " "of \"0\" with base->tp_new. */"); } - tp_flags.append(QLatin1String("|Py_TPFLAGS_HAVE_GC")); } else if (isQApp) { tp_new = QLatin1String("SbkQAppTpNew"); // PYSIDE-571: need singleton app } else { tp_new = QLatin1String("SbkObjectTpNew"); - tp_flags.append(QLatin1String("|Py_TPFLAGS_HAVE_GC")); } + tp_flags.append(QLatin1String("|Py_TPFLAGS_HAVE_GC")); QString tp_richcompare; if (!metaClass->isNamespace() && metaClass->hasComparisonOperatorOverload()) @@ -3953,11 +4230,11 @@ void CppGenerator::writeClassDefinition(QTextStream &s, if (metaClass == miClass) writeMultipleInheritanceInitializerFunction(s, metaClass); writeSpecialCastFunction(s, metaClass); - s << endl; + s << Qt::endl; } - s << "// Class Definition -----------------------------------------------" << endl; - s << "extern \"C\" {" << endl; + s << "// Class Definition -----------------------------------------------\n"; + s << "extern \"C\" {\n"; if (!metaClass->typeEntry()->hashFunction().isEmpty()) tp_hash = QLatin1Char('&') + cpythonBaseName(metaClass) + QLatin1String("_HashFunc"); @@ -3977,14 +4254,14 @@ void CppGenerator::writeClassDefinition(QTextStream &s, suffix = QLatin1String(" *"); const QString typePtr = QLatin1String("_") + className + QLatin1String("_Type"); - s << "static SbkObjectType *" << typePtr << " = nullptr;" << endl; - s << "static SbkObjectType *" << className << "_TypeF(void)" << endl; - s << "{" << endl; - s << INDENT << "return " << typePtr << ";" << endl; - s << "}" << endl; - s << endl; - s << "static PyType_Slot " << className << "_slots[] = {" << endl; - s << INDENT << "{Py_tp_base, nullptr}, // inserted by introduceWrapperType" << endl; + s << "static SbkObjectType *" << typePtr << " = nullptr;\n"; + s << "static SbkObjectType *" << className << "_TypeF(void)\n"; + s << "{\n"; + s << INDENT << "return " << typePtr << ";\n"; + s << "}\n"; + s << Qt::endl; + s << "static PyType_Slot " << className << "_slots[] = {\n"; + s << INDENT << "{Py_tp_base, nullptr}, // inserted by introduceWrapperType\n"; s << INDENT << pyTypeSlotEntry("Py_tp_dealloc", tp_dealloc) << INDENT << pyTypeSlotEntry("Py_tp_repr", m_tpFuncs.value(QLatin1String("__repr__"))) << INDENT << pyTypeSlotEntry("Py_tp_hash", tp_hash) @@ -4002,34 +4279,36 @@ void CppGenerator::writeClassDefinition(QTextStream &s, << INDENT << pyTypeSlotEntry("Py_tp_init", tp_init) << INDENT << pyTypeSlotEntry("Py_tp_new", tp_new); if (supportsSequenceProtocol(metaClass)) { - s << INDENT << "// type supports sequence protocol" << endl; + s << INDENT << "// type supports sequence protocol\n"; writeTypeAsSequenceDefinition(s, metaClass); } if (supportsMappingProtocol(metaClass)) { - s << INDENT << "// type supports mapping protocol" << endl; + s << INDENT << "// type supports mapping protocol\n"; writeTypeAsMappingDefinition(s, metaClass); } if (supportsNumberProtocol(metaClass)) { // This one must come last. See the function itself. - s << INDENT << "// type supports number protocol" << endl; + s << INDENT << "// type supports number protocol\n"; writeTypeAsNumberDefinition(s, metaClass); } - s << INDENT << "{0, " << NULL_PTR << '}' << endl; - s << "};" << endl; - s << "static PyType_Spec " << className << "_spec = {" << endl; - s << INDENT << "\"" << computedClassTargetFullName << "\"," << endl; - s << INDENT << "sizeof(SbkObject)," << endl; - s << INDENT << "0," << endl; - s << INDENT << tp_flags << "," << endl; - s << INDENT << className << "_slots" << endl; - s << "};" << endl; - s << endl; - s << "} //extern \"C\"" << endl; + s << INDENT << "{0, " << NULL_PTR << "}\n"; + s << "};\n"; + + int packageLevel = packageName().count(QLatin1Char('.')) + 1; + s << "static PyType_Spec " << className << "_spec = {\n"; + s << INDENT << '"' << packageLevel << ':' << computedClassTargetFullName << "\",\n"; + s << INDENT << "sizeof(SbkObject),\n"; + s << INDENT << "0,\n"; + s << INDENT << tp_flags << ",\n"; + s << INDENT << className << "_slots\n"; + s << "};\n"; + s << Qt::endl; + s << "} //extern \"C\"" << Qt::endl; } void CppGenerator::writeMappingMethods(QTextStream &s, const AbstractMetaClass *metaClass, - GeneratorContext &context) + const GeneratorContext &context) { for (auto it = m_mappingProtocol.cbegin(), end = m_mappingProtocol.cend(); it != end; ++it) { const AbstractMetaFunction *func = metaClass->findFunction(it.key()); @@ -4040,20 +4319,20 @@ void CppGenerator::writeMappingMethods(QTextStream &s, QString funcRetVal = it.value().second; CodeSnipList snips = func->injectedCodeSnips(TypeSystem::CodeSnipPositionAny, TypeSystem::TargetLangCode); - s << funcRetVal << ' ' << funcName << '(' << funcArgs << ')' << endl << '{' << endl; + s << funcRetVal << ' ' << funcName << '(' << funcArgs << ")\n{\n"; writeInvalidPyObjectCheck(s, QLatin1String("self")); writeCppSelfDefinition(s, func, context); const AbstractMetaArgument *lastArg = func->arguments().isEmpty() ? nullptr : func->arguments().constLast(); writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionAny, TypeSystem::TargetLangCode, func, lastArg); - s << '}' << endl << endl; + s<< "}\n\n"; } } void CppGenerator::writeSequenceMethods(QTextStream &s, const AbstractMetaClass *metaClass, - GeneratorContext &context) + const GeneratorContext &context) { bool injectedCode = false; @@ -4067,18 +4346,18 @@ void CppGenerator::writeSequenceMethods(QTextStream &s, QString funcRetVal = it.value().second; CodeSnipList snips = func->injectedCodeSnips(TypeSystem::CodeSnipPositionAny, TypeSystem::TargetLangCode); - s << funcRetVal << ' ' << funcName << '(' << funcArgs << ')' << endl << '{' << endl; + s << funcRetVal << ' ' << funcName << '(' << funcArgs << ")\n{\n"; writeInvalidPyObjectCheck(s, QLatin1String("self")); writeCppSelfDefinition(s, func, context); - const AbstractMetaArgument *lastArg = func->arguments().isEmpty() ? 0 : func->arguments().constLast(); + const AbstractMetaArgument *lastArg = func->arguments().isEmpty() ? nullptr : func->arguments().constLast(); writeCodeSnips(s, snips,TypeSystem::CodeSnipPositionAny, TypeSystem::TargetLangCode, func, lastArg); - s << '}' << endl << endl; + s<< "}\n\n"; } if (!injectedCode) - writeStdListWrapperMethods(s, context); + writeDefaultSequenceMethods(s, context); } void CppGenerator::writeTypeAsSequenceDefinition(QTextStream &s, const AbstractMetaClass *metaClass) @@ -4107,10 +4386,10 @@ void CppGenerator::writeTypeAsSequenceDefinition(QTextStream &s, const AbstractM if (funcs[sqName].isEmpty()) continue; if (it.value() == QLatin1String("sq_slice")) - s << "#ifndef IS_PY3K" << endl; - s << INDENT << "{Py_" << it.value() << ", (void *)" << funcs[sqName] << "}," << endl; + s << "#ifndef IS_PY3K\n"; + s << INDENT << "{Py_" << it.value() << ", (void *)" << funcs[sqName] << "},\n"; if (it.value() == QLatin1String("sq_slice")) - s << "#endif" << endl; + s << "#endif\n"; } } @@ -4137,7 +4416,7 @@ void CppGenerator::writeTypeAsMappingDefinition(QTextStream &s, const AbstractMe const QString &mpName = it.key(); if (funcs[mpName].isEmpty()) continue; - s << INDENT << "{Py_" << it.value() << ", (void *)" << funcs[mpName] << "}," << endl; + s << INDENT << "{Py_" << it.value() << ", (void *)" << funcs[mpName] << "},\n"; } } @@ -4194,28 +4473,28 @@ void CppGenerator::writeTypeAsNumberDefinition(QTextStream &s, const AbstractMet // bool is special because the field name differs on Python 2 and 3 (nb_nonzero vs nb_bool) // so a shiboken macro is used. if (nbName == QLatin1String("bool")) { - s << "#ifdef IS_PY3K" << endl; - s << INDENT << "{Py_nb_bool, (void *)" << nb[nbName] << "}," << endl; - s << "#else" << endl; - s << INDENT << "{Py_nb_nonzero, (void *)" << nb[nbName] << "}," << endl; - s << "#endif" << endl; + s << "#ifdef IS_PY3K\n"; + s << INDENT << "{Py_nb_bool, (void *)" << nb[nbName] << "},\n"; + s << "#else\n"; + s << INDENT << "{Py_nb_nonzero, (void *)" << nb[nbName] << "},\n"; + s << "#endif\n"; } else { bool excludeFromPy3K = nbName == QLatin1String("__div__") || nbName == QLatin1String("__idiv__"); if (!excludeFromPy3K) - s << INDENT << "{Py_" << it.value() << ", (void *)" << nb[nbName] << "}," << endl; + s << INDENT << "{Py_" << it.value() << ", (void *)" << nb[nbName] << "},\n"; } } if (!nb[QLatin1String("__div__")].isEmpty()) { - s << INDENT << "{Py_nb_true_divide, (void *)" << nb[QLatin1String("__div__")] << "}," << endl; - s << "#ifndef IS_PY3K" << endl; - s << INDENT << "{Py_nb_divide, (void *)" << nb[QLatin1String("__div__")] << "}," << endl; - s << "#endif" << endl; + s << INDENT << "{Py_nb_true_divide, (void *)" << nb[QLatin1String("__div__")] << "},\n"; + s << "#ifndef IS_PY3K\n"; + s << INDENT << "{Py_nb_divide, (void *)" << nb[QLatin1String("__div__")] << "},\n"; + s << "#endif\n"; } if (!nb[QLatin1String("__idiv__")].isEmpty()) { - s << INDENT << "// This function is unused in Python 3. We reference it here." << endl; - s << INDENT << "{0, (void *)" << nb[QLatin1String("__idiv__")] << "}," << endl; - s << INDENT << "// This list is ending at the first 0 entry." << endl; - s << INDENT << "// Therefore, we need to put the unused functions at the very end." << endl; + s << INDENT << "// This function is unused in Python 3. We reference it here.\n"; + s << INDENT << "{0, (void *)" << nb[QLatin1String("__idiv__")] << "},\n"; + s << INDENT << "// This list is ending at the first 0 entry.\n"; + s << INDENT << "// Therefore, we need to put the unused functions at the very end.\n"; } } @@ -4223,28 +4502,28 @@ void CppGenerator::writeTpTraverseFunction(QTextStream &s, const AbstractMetaCla { QString baseName = cpythonBaseName(metaClass); s << "static int "; - s << baseName << "_traverse(PyObject *self, visitproc visit, void *arg)" << endl; - s << '{' << endl; - s << INDENT << "return reinterpret_cast<PyTypeObject *>(SbkObject_TypeF())->tp_traverse(self, visit, arg);" << endl; - s << '}' << endl; + s << baseName << "_traverse(PyObject *self, visitproc visit, void *arg)\n"; + s << "{\n"; + s << INDENT << "return reinterpret_cast<PyTypeObject *>(SbkObject_TypeF())->tp_traverse(self, visit, arg);\n"; + s << "}\n"; } void CppGenerator::writeTpClearFunction(QTextStream &s, const AbstractMetaClass *metaClass) { QString baseName = cpythonBaseName(metaClass); s << "static int "; - s << baseName << "_clear(PyObject *self)" << endl; - s << '{' << endl; - s << INDENT << "return reinterpret_cast<PyTypeObject *>(SbkObject_TypeF())->tp_clear(self);" << endl; - s << '}' << endl; + s << baseName << "_clear(PyObject *self)\n"; + s << "{\n"; + s << INDENT << "return reinterpret_cast<PyTypeObject *>(SbkObject_TypeF())->tp_clear(self);\n"; + s << "}\n"; } -void CppGenerator::writeCopyFunction(QTextStream &s, GeneratorContext &context) +void CppGenerator::writeCopyFunction(QTextStream &s, const GeneratorContext &context) { const AbstractMetaClass *metaClass = context.metaClass(); const QString className = chopType(cpythonTypeName(metaClass)); - s << "static PyObject *" << className << "___copy__(PyObject *self)" << endl; - s << "{" << endl; + s << "static PyObject *" << className << "___copy__(PyObject *self)\n"; + s << "{\n"; writeCppSelfDefinition(s, context, false, true); QString conversionCode; if (!context.forSmartPointer()) @@ -4253,20 +4532,25 @@ void CppGenerator::writeCopyFunction(QTextStream &s, GeneratorContext &context) conversionCode = cpythonToPythonConversionFunction(context.preciseType()); s << INDENT << "PyObject *" << PYTHON_RETURN_VAR << " = " << conversionCode; - s << CPP_SELF_VAR << ");" << endl; + s << CPP_SELF_VAR << ");\n"; writeFunctionReturnErrorCheckSection(s); - s << INDENT << "return " << PYTHON_RETURN_VAR << ";" << endl; - s << "}" << endl; - s << endl; + s << INDENT << "return " << PYTHON_RETURN_VAR << ";\n"; + s << "}\n"; + s << Qt::endl; +} + +static inline void writeGetterFunctionStart(QTextStream &s, const QString &funcName) +{ + s << "static PyObject *" << funcName << "(PyObject *self, void *)\n"; + s << "{\n"; } void CppGenerator::writeGetterFunction(QTextStream &s, const AbstractMetaField *metaField, - GeneratorContext &context) + const GeneratorContext &context) { ErrorCode errorCode(QString::fromLatin1(NULL_PTR)); - s << "static PyObject *" << cpythonGetterFunctionName(metaField) << "(PyObject *self, void *)" << endl; - s << '{' << endl; + writeGetterFunctionStart(s, cpythonGetterFunctionName(metaField)); writeCppSelfDefinition(s, context); @@ -4277,7 +4561,7 @@ void CppGenerator::writeGetterFunction(QTextStream &s, QString cppField; if (avoidProtectedHack() && metaField->isProtected()) { QTextStream(&cppField) << "static_cast<" - << wrapperName(metaField->enclosingClass()) << " *>(" + << context.wrapperName() << " *>(" << CPP_SELF_VAR << ")->" << protectedFieldGetterName(metaField) << "()"; } else { cppField = QLatin1String(CPP_SELF_VAR) + QLatin1String("->") + metaField->name(); @@ -4287,7 +4571,7 @@ void CppGenerator::writeGetterFunction(QTextStream &s, } } if (isCppIntegralPrimitive(fieldType) || fieldType->isEnum()) { - s << INDENT << getFullTypeNameWithoutModifiers(fieldType) << " cppOut_local = " << cppField << ';' << endl; + s << INDENT << getFullTypeNameWithoutModifiers(fieldType) << " cppOut_local = " << cppField << ";\n"; cppField = QLatin1String("cppOut_local"); } else if (avoidProtectedHack() && metaField->isProtected()) { s << INDENT << getFullTypeNameWithoutModifiers(fieldType); @@ -4297,7 +4581,7 @@ void CppGenerator::writeGetterFunction(QTextStream &s, } else if ((!fieldType->isConstant() && !fieldType->isEnum() && !fieldType->isPrimitive()) || fieldType->indirections() == 1) { s << " *"; } - s << " fieldValue = " << cppField << ';' << endl; + s << " fieldValue = " << cppField << ";\n"; cppField = QLatin1String("fieldValue"); } @@ -4314,11 +4598,16 @@ void CppGenerator::writeGetterFunction(QTextStream &s, << "reinterpret_cast<SbkObject *>(self), reinterpret_cast<SbkObjectType *>(" << cpythonTypeNameExt(fieldType) << ")));\n"; - s << INDENT << "if (pyOut) {Py_IncRef(pyOut); return pyOut;}\n"; + s << INDENT << "if (pyOut) {\n"; + { + Indentation indent(INDENT); + s << INDENT << "Py_IncRef(pyOut);\n" + << INDENT << "return pyOut;\n"; + } + s << INDENT << "}\n"; } - s << INDENT << "}\n"; // Check if field wrapper has already been created. - s << INDENT << "else if (Shiboken::BindingManager::instance().hasWrapper(" << cppField << ")) {" << "\n"; + s << INDENT << "} else if (Shiboken::BindingManager::instance().hasWrapper(" << cppField << ")) {" << "\n"; { Indentation indent(INDENT); s << INDENT << "pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(" @@ -4330,98 +4619,144 @@ void CppGenerator::writeGetterFunction(QTextStream &s, // Create and register new wrapper s << INDENT << "pyOut = "; s << "Shiboken::Object::newObject(reinterpret_cast<SbkObjectType *>(" << cpythonTypeNameExt(fieldType) - << "), " << cppField << ", false, true);" << endl; + << "), " << cppField << ", false, true);\n"; s << INDENT << "Shiboken::Object::setParent(self, pyOut)"; } else { s << INDENT << "pyOut = "; writeToPythonConversion(s, fieldType, metaField->enclosingClass(), cppField); } - s << ';' << endl; + s << ";\n"; - s << INDENT << "return pyOut;" << endl; - s << '}' << endl; + s << INDENT << "return pyOut;\n"; + s << "}\n"; } -void CppGenerator::writeSetterFunction(QTextStream &s, - const AbstractMetaField *metaField, - GeneratorContext &context) +// Write a getter for QPropertySpec +void CppGenerator::writeGetterFunction(QTextStream &s, const QPropertySpec *property, + const GeneratorContext &context) { ErrorCode errorCode(0); - s << "static int " << cpythonSetterFunctionName(metaField) << "(PyObject *self, PyObject *pyIn, void *)" << endl; - s << '{' << endl; - + writeGetterFunctionStart(s, cpythonGetterFunctionName(property, context.metaClass())); writeCppSelfDefinition(s, context); - - s << INDENT << "if (pyIn == " << NULL_PTR << ") {" << endl; + const QString value = QStringLiteral("value"); + s << INDENT << "auto " << value << " = " << CPP_SELF_VAR << "->" << property->read() << "();\n" + << INDENT << "auto pyResult = "; + writeToPythonConversion(s, property->type(), context.metaClass(), value); + s << ";\n" + << INDENT << "if (PyErr_Occurred() || !pyResult) {\n"; { Indentation indent(INDENT); - s << INDENT << "PyErr_SetString(PyExc_TypeError, \"'"; - s << metaField->name() << "' may not be deleted\");" << endl; - s << INDENT << "return -1;" << endl; + s << INDENT << "Py_XDECREF(pyResult);\n" + << INDENT << " return {};\n"; } - s << INDENT << '}' << endl; + s << INDENT << "}\n" + << INDENT << "return pyResult;\n}\n\n"; +} - AbstractMetaType *fieldType = metaField->type(); +// Write setter function preamble (type checks on "pyIn") +void CppGenerator::writeSetterFunctionPreamble(QTextStream &s, const QString &name, + const QString &funcName, + const AbstractMetaType *type, + const GeneratorContext &context) +{ + s << "static int " << funcName << "(PyObject *self, PyObject *pyIn, void *)\n"; + s << "{\n"; + + writeCppSelfDefinition(s, context); - s << INDENT << "PythonToCppFunc " << PYTHON_TO_CPP_VAR << "{nullptr};" << endl; + s << INDENT << "if (pyIn == " << NULL_PTR << ") {\n" << indent(INDENT) + << INDENT << "PyErr_SetString(PyExc_TypeError, \"'" + << name << "' may not be deleted\");\n" + << INDENT << "return -1;\n" + << outdent(INDENT) << INDENT << "}\n"; + + s << INDENT << "PythonToCppFunc " << PYTHON_TO_CPP_VAR << "{nullptr};\n"; s << INDENT << "if (!"; - writeTypeCheck(s, fieldType, QLatin1String("pyIn"), isNumber(fieldType->typeEntry())); - s << ") {" << endl; - { - Indentation indent(INDENT); - s << INDENT << "PyErr_SetString(PyExc_TypeError, \"wrong type attributed to '"; - s << metaField->name() << "', '" << fieldType->name() << "' or convertible type expected\");" << endl; - s << INDENT << "return -1;" << endl; - } - s << INDENT << '}' << endl << endl; + writeTypeCheck(s, type, QLatin1String("pyIn"), isNumber(type->typeEntry())); + s << ") {\n" << indent(INDENT) + << INDENT << "PyErr_SetString(PyExc_TypeError, \"wrong type attributed to '" + << name << "', '" << type->name() << "' or convertible type expected\");\n" + << INDENT << "return -1;\n" + << outdent(INDENT) << INDENT<< "}\n\n"; +} + +void CppGenerator::writeSetterFunction(QTextStream &s, + const AbstractMetaField *metaField, + const GeneratorContext &context) +{ + ErrorCode errorCode(0); + + AbstractMetaType *fieldType = metaField->type(); + writeSetterFunctionPreamble(s, metaField->name(), cpythonSetterFunctionName(metaField), + fieldType, context); QString cppField = QString::fromLatin1("%1->%2").arg(QLatin1String(CPP_SELF_VAR), metaField->name()); s << INDENT; if (avoidProtectedHack() && metaField->isProtected()) { s << getFullTypeNameWithoutModifiers(fieldType); - s << (fieldType->indirections() == 1 ? " *" : "") << " cppOut;" << endl; - s << INDENT << PYTHON_TO_CPP_VAR << "(pyIn, &cppOut);" << endl; - s << INDENT << "static_cast<" << wrapperName(metaField->enclosingClass()) + s << (fieldType->indirections() == 1 ? " *" : "") << " cppOut;\n"; + s << INDENT << PYTHON_TO_CPP_VAR << "(pyIn, &cppOut);\n"; + s << INDENT << "static_cast<" << context.wrapperName() << " *>(" << CPP_SELF_VAR << ")->" << protectedFieldSetterName(metaField) << "(cppOut)"; } else if (isCppIntegralPrimitive(fieldType) || fieldType->typeEntry()->isEnum() || fieldType->typeEntry()->isFlags()) { - s << getFullTypeNameWithoutModifiers(fieldType) << " cppOut_local = " << cppField << ';' << endl; - s << INDENT << PYTHON_TO_CPP_VAR << "(pyIn, &cppOut_local);" << endl; + s << getFullTypeNameWithoutModifiers(fieldType) << " cppOut_local = " << cppField << ";\n"; + s << INDENT << PYTHON_TO_CPP_VAR << "(pyIn, &cppOut_local);\n"; s << INDENT << cppField << " = cppOut_local"; } else { if (isPointerToConst(fieldType)) s << "const "; s << getFullTypeNameWithoutModifiers(fieldType); s << QString::fromLatin1(" *").repeated(fieldType->indirections()) << "& cppOut_ptr = "; - s << cppField << ';' << endl; + s << cppField << ";\n"; s << INDENT << PYTHON_TO_CPP_VAR << "(pyIn, &cppOut_ptr)"; } - s << ';' << endl << endl; + s << ";\n" << Qt::endl; if (isPointerToWrapperType(fieldType)) { s << INDENT << "Shiboken::Object::keepReference(reinterpret_cast<SbkObject *>(self), \""; - s << metaField->name() << "\", pyIn);" << endl; + s << metaField->name() << "\", pyIn);\n"; } - s << INDENT << "return 0;" << endl; - s << '}' << endl; + s << INDENT << "return 0;\n"; + s << "}\n"; +} + +// Write a setter for QPropertySpec +void CppGenerator::writeSetterFunction(QTextStream &s, const QPropertySpec *property, + const GeneratorContext &context) +{ + ErrorCode errorCode(0); + writeSetterFunctionPreamble(s, property->name(), + cpythonSetterFunctionName(property, context.metaClass()), + property->type(), context); + + s << INDENT << "auto cppOut = " << CPP_SELF_VAR << "->" << property->read() << "();\n" + << INDENT << PYTHON_TO_CPP_VAR << "(pyIn, &cppOut);\n" + << INDENT << "if (PyErr_Occurred())\n"; + { + Indentation indent(INDENT); + s << INDENT << "return -1;\n"; + } + s << INDENT << CPP_SELF_VAR << "->" << property->write() << "(cppOut);\n" + << INDENT << "return 0;\n}\n\n"; } -void CppGenerator::writeRichCompareFunction(QTextStream &s, GeneratorContext &context) +void CppGenerator::writeRichCompareFunction(QTextStream &s, const GeneratorContext &context) { const AbstractMetaClass *metaClass = context.metaClass(); QString baseName = cpythonBaseName(metaClass); s << "static PyObject * "; - s << baseName << "_richcompare(PyObject *self, PyObject *" << PYTHON_ARG << ", int op)" << endl; - s << '{' << endl; + s << baseName << "_richcompare(PyObject *self, PyObject *" << PYTHON_ARG + << ", int op)\n{\n"; writeCppSelfDefinition(s, context, false, true); writeUnusedVariableCast(s, QLatin1String(CPP_SELF_VAR)); - s << INDENT << "PyObject *" << PYTHON_RETURN_VAR << "{};" << endl; - s << INDENT << "PythonToCppFunc " << PYTHON_TO_CPP_VAR << ';' << endl; + s << INDENT << "PyObject *" << PYTHON_RETURN_VAR << "{};\n"; + s << INDENT << "PythonToCppFunc " << PYTHON_TO_CPP_VAR << ";\n"; writeUnusedVariableCast(s, QLatin1String(PYTHON_TO_CPP_VAR)); - s << endl; + s << Qt::endl; - s << INDENT << "switch (op) {" << endl; + s << INDENT << "switch (op) {\n"; { Indentation indent(INDENT); const QVector<AbstractMetaFunctionList> &groupedFuncs = filterGroupedOperatorFunctions(metaClass, AbstractMetaClass::ComparisonOp); @@ -4429,7 +4764,7 @@ void CppGenerator::writeRichCompareFunction(QTextStream &s, GeneratorContext &co const AbstractMetaFunction *rfunc = overloads[0]; QString operatorId = ShibokenGenerator::pythonRichCompareOperatorId(rfunc); - s << INDENT << "case " << operatorId << ':' << endl; + s << INDENT << "case " << operatorId << ':' << Qt::endl; Indentation indent(INDENT); @@ -4461,21 +4796,28 @@ void CppGenerator::writeRichCompareFunction(QTextStream &s, GeneratorContext &co } s << "if ("; writeTypeCheck(s, argType, QLatin1String(PYTHON_ARG), alternativeNumericTypes == 1 || isPyInt(argType)); - s << ") {" << endl; + s << ") {\n"; { Indentation indent(INDENT); - s << INDENT << "// " << func->signature() << endl; + s << INDENT << "// " << func->signature() << Qt::endl; writeArgumentConversion(s, argType, QLatin1String(CPP_ARG0), QLatin1String(PYTHON_ARG), metaClass, QString(), func->isUserAdded()); // If the function is user added, use the inject code + bool generateOperatorCode = true; if (func->isUserAdded()) { CodeSnipList snips = func->injectedCodeSnips(); - writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionAny, TypeSystem::TargetLangCode, func, func->arguments().constLast()); - } else { + if (!snips.isEmpty()) { + writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionAny, + TypeSystem::TargetLangCode, func, + func->arguments().constLast()); + generateOperatorCode = false; + } + } + if (generateOperatorCode) { s << INDENT; - if (func->type()) + if (!func->isVoid()) s << func->type()->cppSignature() << " " << CPP_RETURN_VAR << " = "; // expression if (func->isPointerOperator()) @@ -4483,49 +4825,51 @@ void CppGenerator::writeRichCompareFunction(QTextStream &s, GeneratorContext &co s << CPP_SELF_VAR << ' ' << op << '('; if (shouldDereferenceAbstractMetaTypePointer(argType)) s << '*'; - s << CPP_ARG0 << ");" << endl; + s << CPP_ARG0 << ");\n"; s << INDENT << PYTHON_RETURN_VAR << " = "; - if (func->type()) + if (!func->isVoid()) writeToPythonConversion(s, func->type(), metaClass, QLatin1String(CPP_RETURN_VAR)); else - s << "Py_None;" << endl << INDENT << "Py_INCREF(Py_None)"; - s << ';' << endl; + s << "Py_None;\n" << INDENT << "Py_INCREF(Py_None)"; + s << ";\n"; } } s << INDENT << '}'; } - s << " else {" << endl; + s << " else {\n"; if (operatorId == QLatin1String("Py_EQ") || operatorId == QLatin1String("Py_NE")) { Indentation indent(INDENT); s << INDENT << PYTHON_RETURN_VAR << " = " - << (operatorId == QLatin1String("Py_EQ") ? "Py_False" : "Py_True") << ';' << endl; - s << INDENT << "Py_INCREF(" << PYTHON_RETURN_VAR << ");" << endl; + << (operatorId == QLatin1String("Py_EQ") ? "Py_False" : "Py_True") << ";\n"; + s << INDENT << "Py_INCREF(" << PYTHON_RETURN_VAR << ");\n"; } else { Indentation indent(INDENT); - s << INDENT << "goto " << baseName << "_RichComparison_TypeError;" << endl; + s << INDENT << "goto " << baseName << "_RichComparison_TypeError;\n"; } - s << INDENT << '}' << endl << endl; + s << INDENT<< "}\n\n"; - s << INDENT << "break;" << endl; + s << INDENT << "break;\n"; } - s << INDENT << "default:" << endl; + s << INDENT << "default:\n"; { Indentation indent(INDENT); - s << INDENT << "goto " << baseName << "_RichComparison_TypeError;" << endl; + s << INDENT << "// PYSIDE-74: By default, we redirect to object's tp_richcompare (which is `==`, `!=`).\n"; + s << INDENT << "return FallbackRichCompare(self, " << PYTHON_ARG << ", op);\n"; + s << INDENT << "goto " << baseName << "_RichComparison_TypeError;\n"; } } - s << INDENT << '}' << endl << endl; + s << INDENT<< "}\n\n"; - s << INDENT << "if (" << PYTHON_RETURN_VAR << " && !PyErr_Occurred())" << endl; + s << INDENT << "if (" << PYTHON_RETURN_VAR << " && !PyErr_Occurred())\n"; { Indentation indent(INDENT); - s << INDENT << "return " << PYTHON_RETURN_VAR << ";" << endl; + s << INDENT << "return " << PYTHON_RETURN_VAR << ";\n"; } - s << INDENT << baseName << "_RichComparison_TypeError:" << endl; - s << INDENT << "PyErr_SetString(PyExc_NotImplementedError, \"operator not implemented.\");" << endl; - s << INDENT << returnStatement(m_currentErrorCode) << endl << endl; - s << '}' << endl << endl; + s << INDENT << baseName << "_RichComparison_TypeError:\n"; + s << INDENT << "PyErr_SetString(PyExc_NotImplementedError, \"operator not implemented.\");\n"; + s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl << Qt::endl; + s<< "}\n\n"; } void CppGenerator::writeMethodDefinitionEntry(QTextStream &s, const AbstractMetaFunctionList &overloads) @@ -4549,8 +4893,14 @@ void CppGenerator::writeMethodDefinitionEntry(QTextStream &s, const AbstractMeta if (overloadData.hasArgumentWithDefaultValue()) s << "|METH_KEYWORDS"; } - if (func->ownerClass() && overloadData.hasStaticFunction()) + // METH_STATIC causes a crash when used for global functions (also from + // invisible namespaces). + auto ownerClass = func->ownerClass(); + if (ownerClass + && !invisibleTopNamespaces().contains(const_cast<AbstractMetaClass *>(ownerClass)) + && overloadData.hasStaticFunction()) { s << "|METH_STATIC"; + } } void CppGenerator::writeMethodDefinition(QTextStream &s, const AbstractMetaFunctionList &overloads) @@ -4568,20 +4918,24 @@ void CppGenerator::writeMethodDefinition(QTextStream &s, const AbstractMetaFunct writeMethodDefinitionEntry(s, overloads); s << '}'; } - s << ',' << endl; + s << ',' << Qt::endl; } void CppGenerator::writeSignatureInfo(QTextStream &s, const AbstractMetaFunctionList &overloads) { OverloadData overloadData(overloads, this); const AbstractMetaFunction *rfunc = overloadData.referenceFunction(); - QString funcName = fullPythonFunctionName(rfunc); + QString funcName = fullPythonFunctionName(rfunc, false); int idx = overloads.length() - 1; bool multiple = idx > 0; for (const AbstractMetaFunction *f : overloads) { QStringList args; + // PYSIDE-1328: `self`-ness cannot be computed in Python because there are mixed cases. + // Toplevel functions like `PySide2.QtCore.QEnum` are always self-less. + if (!(f->isStatic()) && f->ownerClass()) + args << QLatin1String("self"); const AbstractMetaArgumentList &arguments = f->arguments(); for (const AbstractMetaArgument *arg : arguments) { QString strArg = arg->type()->pythonSignature(); @@ -4589,8 +4943,6 @@ void CppGenerator::writeSignatureInfo(QTextStream &s, const AbstractMetaFunction strArg += QLatin1Char('='); QString e = arg->defaultValueExpression(); e.replace(QLatin1String("::"), QLatin1String(".")); - // the tests insert stuff like Str("<unknown>"): - e.replace(QLatin1Char('"'), QLatin1String("\\\"")); strArg += e; } args << arg->name() + QLatin1Char(':') + strArg; @@ -4599,9 +4951,9 @@ void CppGenerator::writeSignatureInfo(QTextStream &s, const AbstractMetaFunction if (multiple) s << idx-- << ':'; s << funcName << '(' << args.join(QLatin1Char(',')) << ')'; - if (f->type()) + if (!f->isVoid()) s << "->" << f->type()->pythonSignature(); - s << endl; + s << Qt::endl; } } @@ -4609,7 +4961,7 @@ void CppGenerator::writeEnumsInitialization(QTextStream &s, AbstractMetaEnumList { if (enums.isEmpty()) return; - s << INDENT << "// Initialization of enums." << endl << endl; + s << INDENT << "// Initialization of enums.\n\n"; for (const AbstractMetaEnum *cppEnum : qAsConst(enums)) { if (cppEnum->isPrivate()) continue; @@ -4628,9 +4980,8 @@ static QString mangleName(QString name) void CppGenerator::writeEnumInitialization(QTextStream &s, const AbstractMetaEnum *cppEnum) { - const AbstractMetaClass *enclosingClass = getProperEnclosingClassForEnum(cppEnum); - const AbstractMetaClass *upper = enclosingClass ? enclosingClass->enclosingClass() : nullptr; - bool hasUpperEnclosingClass = upper && upper->typeEntry()->codeGeneration() != TypeEntry::GenerateForSubclass; + const AbstractMetaClass *enclosingClass = cppEnum->targetLangEnclosingClass(); + bool hasUpperEnclosingClass = enclosingClass && enclosingClass->targetLangEnclosingClass() != nullptr; const EnumTypeEntry *enumTypeEntry = cppEnum->typeEntry(); QString enclosingObjectVariable; if (enclosingClass) @@ -4642,10 +4993,11 @@ void CppGenerator::writeEnumInitialization(QTextStream &s, const AbstractMetaEnu s << INDENT << "// Initialization of "; s << (cppEnum->isAnonymous() ? "anonymous enum identified by enum value" : "enum"); - s << " '" << cppEnum->name() << "'." << endl; + s << " '" << cppEnum->name() << "'.\n"; QString enumVarTypeObj; if (!cppEnum->isAnonymous()) { + int packageLevel = packageName().count(QLatin1Char('.')) + 1; FlagsTypeEntry *flags = enumTypeEntry->flags(); if (flags) { // The following could probably be made nicer: @@ -4653,29 +5005,29 @@ void CppGenerator::writeEnumInitialization(QTextStream &s, const AbstractMetaEnu QString fullPath = getClassTargetFullName(cppEnum); fullPath.truncate(fullPath.lastIndexOf(QLatin1Char('.')) + 1); s << INDENT << cpythonTypeNameExt(flags) << " = PySide::QFlags::create(\"" - << fullPath << flags->flagsName() << "\", " - << cpythonEnumName(cppEnum) << "_number_slots);" << endl; + << packageLevel << ':' << fullPath << flags->flagsName() << "\", " + << cpythonEnumName(cppEnum) << "_number_slots);\n"; } enumVarTypeObj = cpythonTypeNameExt(enumTypeEntry); s << INDENT << enumVarTypeObj << " = Shiboken::Enum::"; s << ((enclosingClass || hasUpperEnclosingClass) ? "createScopedEnum" : "createGlobalEnum"); - s << '(' << enclosingObjectVariable << ',' << endl; + s << '(' << enclosingObjectVariable << ',' << Qt::endl; { Indentation indent(INDENT); - s << INDENT << '"' << cppEnum->name() << "\"," << endl; - s << INDENT << '"' << getClassTargetFullName(cppEnum) << "\"," << endl; + s << INDENT << '"' << cppEnum->name() << "\",\n"; + s << INDENT << '"' << packageLevel << ':' << getClassTargetFullName(cppEnum) << "\",\n"; s << INDENT << '"' << (cppEnum->enclosingClass() ? (cppEnum->enclosingClass()->qualifiedCppName() + QLatin1String("::")) : QString()); s << cppEnum->name() << '"'; if (flags) - s << ',' << endl << INDENT << cpythonTypeNameExt(flags); - s << ");" << endl; + s << ',' << Qt::endl << INDENT << cpythonTypeNameExt(flags); + s << ");\n"; } - s << INDENT << "if (!" << cpythonTypeNameExt(cppEnum->typeEntry()) << ')' << endl; + s << INDENT << "if (!" << cpythonTypeNameExt(cppEnum->typeEntry()) << ")\n"; { Indentation indent(INDENT); - s << INDENT << returnStatement(m_currentErrorCode) << endl << endl; + s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl << Qt::endl; } } @@ -4700,45 +5052,45 @@ void CppGenerator::writeEnumInitialization(QTextStream &s, const AbstractMetaEnu switch (cppEnum->enumKind()) { case AnonymousEnum: if (enclosingClass || hasUpperEnclosingClass) { - s << INDENT << '{' << endl; + s << INDENT << "{\n"; { Indentation indent(INDENT); - s << INDENT << "PyObject *anonEnumItem = PyInt_FromLong(" << enumValueText << ");" << endl; + s << INDENT << "PyObject *anonEnumItem = PyInt_FromLong(" << enumValueText << ");\n"; s << INDENT << "if (PyDict_SetItemString(reinterpret_cast<PyTypeObject *>(reinterpret_cast<SbkObjectType *>(" << enclosingObjectVariable - << "))->tp_dict, \"" << mangleName(enumValue->name()) << "\", anonEnumItem) < 0)" << endl; + << "))->tp_dict, \"" << mangleName(enumValue->name()) << "\", anonEnumItem) < 0)\n"; { Indentation indent(INDENT); - s << INDENT << returnStatement(m_currentErrorCode) << endl; + s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl; } - s << INDENT << "Py_DECREF(anonEnumItem);" << endl; + s << INDENT << "Py_DECREF(anonEnumItem);\n"; } - s << INDENT << '}' << endl; + s << INDENT << "}\n"; } else { s << INDENT << "if (PyModule_AddIntConstant(module, \"" << mangleName(enumValue->name()) << "\", "; - s << enumValueText << ") < 0)" << endl; + s << enumValueText << ") < 0)\n"; { Indentation indent(INDENT); - s << INDENT << returnStatement(m_currentErrorCode) << endl; + s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl; } } break; case CEnum: { s << INDENT << "if (!Shiboken::Enum::"; s << ((enclosingClass || hasUpperEnclosingClass) ? "createScopedEnumItem" : "createGlobalEnumItem"); - s << '(' << enumVarTypeObj << ',' << endl; + s << '(' << enumVarTypeObj << ',' << Qt::endl; Indentation indent(INDENT); s << INDENT << enclosingObjectVariable << ", \"" << mangleName(enumValue->name()) << "\", "; - s << enumValueText << "))" << endl; - s << INDENT << returnStatement(m_currentErrorCode) << endl; + s << enumValueText << "))\n"; + s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl; } break; case EnumClass: { s << INDENT << "if (!Shiboken::Enum::createScopedEnumItem(" - << enumVarTypeObj << ',' << endl; + << enumVarTypeObj << ',' << Qt::endl; Indentation indent(INDENT); s << INDENT << enumVarTypeObj<< ", \"" << mangleName(enumValue->name()) << "\", " - << enumValueText << "))" << endl - << INDENT << returnStatement(m_currentErrorCode) << endl; + << enumValueText << "))\n" + << INDENT << returnStatement(m_currentErrorCode) << Qt::endl; } break; } @@ -4749,7 +5101,7 @@ void CppGenerator::writeEnumInitialization(QTextStream &s, const AbstractMetaEnu s << INDENT << "// End of '" << cppEnum->name() << "' enum"; if (cppEnum->typeEntry()->flags()) s << "/flags"; - s << '.' << endl << endl; + s << '.' << Qt::endl << Qt::endl; } void CppGenerator::writeSignalInitialization(QTextStream &s, const AbstractMetaClass *metaClass) @@ -4775,7 +5127,7 @@ void CppGenerator::writeSignalInitialization(QTextStream &s, const AbstractMetaC } s << INDENT << "PySide::Signal::registerSignals(" << cpythonTypeName(metaClass) << ", &::" - << metaClass->qualifiedCppName() << "::staticMetaObject);" << endl; + << metaClass->qualifiedCppName() << "::staticMetaObject);\n"; } void CppGenerator::writeFlagsToLong(QTextStream &s, const AbstractMetaEnum *cppEnum) @@ -4783,13 +5135,13 @@ void CppGenerator::writeFlagsToLong(QTextStream &s, const AbstractMetaEnum *cppE FlagsTypeEntry *flagsEntry = cppEnum->typeEntry()->flags(); if (!flagsEntry) return; - s << "static PyObject *" << cpythonEnumName(cppEnum) << "_long(PyObject *self)" << endl; - s << "{" << endl; - s << INDENT << "int val;" << endl; + s << "static PyObject *" << cpythonEnumName(cppEnum) << "_long(PyObject *self)\n"; + s << "{\n"; + s << INDENT << "int val;\n"; AbstractMetaType *flagsType = buildAbstractMetaTypeFromTypeEntry(flagsEntry); - s << INDENT << cpythonToCppConversionFunction(flagsType) << "self, &val);" << endl; - s << INDENT << "return Shiboken::Conversions::copyToPython(Shiboken::Conversions::PrimitiveTypeConverter<int>(), &val);" << endl; - s << "}" << endl; + s << INDENT << cpythonToCppConversionFunction(flagsType) << "self, &val);\n"; + s << INDENT << "return Shiboken::Conversions::copyToPython(Shiboken::Conversions::PrimitiveTypeConverter<int>(), &val);\n"; + s << "}\n"; } void CppGenerator::writeFlagsNonZero(QTextStream &s, const AbstractMetaEnum *cppEnum) @@ -4797,14 +5149,14 @@ void CppGenerator::writeFlagsNonZero(QTextStream &s, const AbstractMetaEnum *cpp FlagsTypeEntry *flagsEntry = cppEnum->typeEntry()->flags(); if (!flagsEntry) return; - s << "static int " << cpythonEnumName(cppEnum) << "__nonzero(PyObject *self)" << endl; - s << "{" << endl; + s << "static int " << cpythonEnumName(cppEnum) << "__nonzero(PyObject *self)\n"; + s << "{\n"; - s << INDENT << "int val;" << endl; + s << INDENT << "int val;\n"; AbstractMetaType *flagsType = buildAbstractMetaTypeFromTypeEntry(flagsEntry); - s << INDENT << cpythonToCppConversionFunction(flagsType) << "self, &val);" << endl; - s << INDENT << "return val != 0;" << endl; - s << "}" << endl; + s << INDENT << cpythonToCppConversionFunction(flagsType) << "self, &val);\n"; + s << INDENT << "return val != 0;\n"; + s << "}\n"; } void CppGenerator::writeFlagsMethods(QTextStream &s, const AbstractMetaEnum *cppEnum) @@ -4817,30 +5169,42 @@ void CppGenerator::writeFlagsMethods(QTextStream &s, const AbstractMetaEnum *cpp writeFlagsToLong(s, cppEnum); writeFlagsNonZero(s, cppEnum); - s << endl; + s << Qt::endl; } void CppGenerator::writeFlagsNumberMethodsDefinition(QTextStream &s, const AbstractMetaEnum *cppEnum) { QString cpythonName = cpythonEnumName(cppEnum); - s << "static PyType_Slot " << cpythonName << "_number_slots[] = {" << endl; - s << "#ifdef IS_PY3K" << endl; - s << INDENT << "{Py_nb_bool, (void *)" << cpythonName << "__nonzero}," << endl; - s << "#else" << endl; - s << INDENT << "{Py_nb_nonzero, (void *)" << cpythonName << "__nonzero}," << endl; - s << INDENT << "{Py_nb_long, (void *)" << cpythonName << "_long}," << endl; - s << "#endif" << endl; - s << INDENT << "{Py_nb_invert, (void *)" << cpythonName << "___invert__}," << endl; - s << INDENT << "{Py_nb_and, (void *)" << cpythonName << "___and__}," << endl; - s << INDENT << "{Py_nb_xor, (void *)" << cpythonName << "___xor__}," << endl; - s << INDENT << "{Py_nb_or, (void *)" << cpythonName << "___or__}," << endl; - s << INDENT << "{Py_nb_int, (void *)" << cpythonName << "_long}," << endl; - s << "#ifndef IS_PY3K" << endl; - s << INDENT << "{Py_nb_long, (void *)" << cpythonName << "_long}," << endl; - s << "#endif" << endl; - s << INDENT << "{0, " << NULL_PTR << "} // sentinel" << endl; - s << "};" << endl << endl; + s << "static PyType_Slot " << cpythonName << "_number_slots[] = {\n"; + s << "#ifdef IS_PY3K\n"; + s << INDENT << "{Py_nb_bool, reinterpret_cast<void *>(" << cpythonName << "__nonzero)},\n"; + s << "#else\n"; + s << INDENT << "{Py_nb_nonzero, reinterpret_cast<void *>(" << cpythonName << "__nonzero)},\n"; + s << INDENT << "{Py_nb_long, reinterpret_cast<void *>(" << cpythonName << "_long)},\n"; + s << "#endif\n"; + s << INDENT << "{Py_nb_invert, reinterpret_cast<void *>(" << cpythonName << "___invert__)},\n"; + s << INDENT << "{Py_nb_and, reinterpret_cast<void *>(" << cpythonName << "___and__)},\n"; + s << INDENT << "{Py_nb_xor, reinterpret_cast<void *>(" << cpythonName << "___xor__)},\n"; + s << INDENT << "{Py_nb_or, reinterpret_cast<void *>(" << cpythonName << "___or__)},\n"; + s << INDENT << "{Py_nb_int, reinterpret_cast<void *>(" << cpythonName << "_long)},\n"; + s << INDENT << "{Py_nb_index, reinterpret_cast<void *>(" << cpythonName << "_long)},\n"; + s << "#ifndef IS_PY3K\n"; + s << INDENT << "{Py_nb_long, reinterpret_cast<void *>(" << cpythonName << "_long)},\n"; + s << "#endif\n"; + s << INDENT << "{0, " << NULL_PTR << "} // sentinel\n"; + s << "};\n\n"; +} + +void CppGenerator::writeFlagsNumberMethodsDefinitions(QTextStream &s, const AbstractMetaEnumList &enums) +{ + for (AbstractMetaEnum *e : enums) { + if (!e->isAnonymous() && !e->isPrivate() && e->typeEntry()->flags()) { + writeFlagsMethods(s, e); + writeFlagsNumberMethodsDefinition(s, e); + s << '\n'; + } + } } void CppGenerator::writeFlagsBinaryOperator(QTextStream &s, const AbstractMetaEnum *cppEnum, @@ -4849,27 +5213,29 @@ void CppGenerator::writeFlagsBinaryOperator(QTextStream &s, const AbstractMetaEn FlagsTypeEntry *flagsEntry = cppEnum->typeEntry()->flags(); Q_ASSERT(flagsEntry); - s << "PyObject * " << cpythonEnumName(cppEnum) << "___" << pyOpName << "__(PyObject *self, PyObject *" << PYTHON_ARG << ")" << endl; - s << '{' << endl; + s << "PyObject *" << cpythonEnumName(cppEnum) << "___" << pyOpName + << "__(PyObject *self, PyObject *" << PYTHON_ARG << ")\n{\n"; AbstractMetaType *flagsType = buildAbstractMetaTypeFromTypeEntry(flagsEntry); - s << INDENT << "::" << flagsEntry->originalName() << " cppResult, " << CPP_SELF_VAR << ", cppArg;" << endl; - s << "#ifdef IS_PY3K" << endl; + s << INDENT << "::" << flagsEntry->originalName() << " cppResult, " << CPP_SELF_VAR << ", cppArg;\n"; + s << "#ifdef IS_PY3K\n"; s << INDENT << CPP_SELF_VAR << " = static_cast<::" << flagsEntry->originalName() - << ">(int(PyLong_AsLong(self)));" << endl; + << ">(int(PyLong_AsLong(self)));\n"; s << INDENT << "cppArg = static_cast<" << flagsEntry->originalName() << ">(int(PyLong_AsLong(" - << PYTHON_ARG << ")));" << endl; - s << "#else" << endl; + << PYTHON_ARG << ")));\n"; + s << "#else\n"; s << INDENT << CPP_SELF_VAR << " = static_cast<::" << flagsEntry->originalName() - << ">(int(PyInt_AsLong(self)));" << endl; + << ">(int(PyInt_AsLong(self)));\n"; s << INDENT << "cppArg = static_cast<" << flagsEntry->originalName() - << ">(int(PyInt_AsLong(" << PYTHON_ARG << ")));" << endl; - s << "#endif" << endl << endl; - s << INDENT << "cppResult = " << CPP_SELF_VAR << " " << cppOpName << " cppArg;" << endl; + << ">(int(PyInt_AsLong(" << PYTHON_ARG << ")));\n"; + s << "#endif\n\n"; + s << INDENT << "if (PyErr_Occurred())\n" << indent(INDENT) + << INDENT << "return nullptr;\n" << outdent(INDENT); + s << INDENT << "cppResult = " << CPP_SELF_VAR << " " << cppOpName << " cppArg;\n"; s << INDENT << "return "; writeToPythonConversion(s, flagsType, nullptr, QLatin1String("cppResult")); - s << ';' << endl; - s << '}' << endl << endl; + s << ";\n"; + s<< "}\n\n"; } void CppGenerator::writeFlagsUnaryOperator(QTextStream &s, const AbstractMetaEnum *cppEnum, @@ -4879,25 +5245,25 @@ void CppGenerator::writeFlagsUnaryOperator(QTextStream &s, const AbstractMetaEnu FlagsTypeEntry *flagsEntry = cppEnum->typeEntry()->flags(); Q_ASSERT(flagsEntry); - s << "PyObject *" << cpythonEnumName(cppEnum) << "___" << pyOpName << "__(PyObject *self, PyObject *" << PYTHON_ARG << ")" << endl; - s << '{' << endl; + s << "PyObject *" << cpythonEnumName(cppEnum) << "___" << pyOpName + << "__(PyObject *self, PyObject *" << PYTHON_ARG << ")\n{\n"; AbstractMetaType *flagsType = buildAbstractMetaTypeFromTypeEntry(flagsEntry); - s << INDENT << "::" << flagsEntry->originalName() << " " << CPP_SELF_VAR << ";" << endl; - s << INDENT << cpythonToCppConversionFunction(flagsType) << "self, &" << CPP_SELF_VAR << ");" << endl; + s << INDENT << "::" << flagsEntry->originalName() << " " << CPP_SELF_VAR << ";\n"; + s << INDENT << cpythonToCppConversionFunction(flagsType) << "self, &" << CPP_SELF_VAR << ");\n"; s << INDENT; if (boolResult) s << "bool"; else s << "::" << flagsEntry->originalName(); - s << " cppResult = " << cppOpName << CPP_SELF_VAR << ';' << endl; + s << " cppResult = " << cppOpName << CPP_SELF_VAR << ";\n"; s << INDENT << "return "; if (boolResult) s << "PyBool_FromLong(cppResult)"; else writeToPythonConversion(s, flagsType, nullptr, QLatin1String("cppResult")); - s << ';' << endl; - s << '}' << endl << endl; + s << ";\n"; + s<< "}\n\n"; } QString CppGenerator::getSimpleClassInitFunctionName(const AbstractMetaClass *metaClass) const @@ -4911,64 +5277,75 @@ QString CppGenerator::getSimpleClassInitFunctionName(const AbstractMetaClass *me return initFunctionName; } -QString CppGenerator::getInitFunctionName(GeneratorContext &context) const +QString CppGenerator::getInitFunctionName(const GeneratorContext &context) const { return !context.forSmartPointer() ? getSimpleClassInitFunctionName(context.metaClass()) : getFilteredCppSignatureString(context.preciseType()->cppSignature()); } +void CppGenerator::writeSignatureStrings(QTextStream &s, + QTextStream &signatureStream, + const QString &arrayName, + const char *comment) const +{ + s << "// The signatures string for the " << comment << ".\n"; + s << "// Multiple signatures have their index \"n:\" in front.\n"; + s << "static const char *" << arrayName << "_SignatureStrings[] = {\n"; + QString line; + while (signatureStream.readLineInto(&line)) { + // must anything be escaped? + if (line.contains(QLatin1Char('"')) || line.contains(QLatin1Char('\\'))) + s << INDENT << "R\"CPP(" << line << ")CPP\",\n"; + else + s << INDENT << '"' << line << "\",\n"; + } + s << INDENT << NULL_PTR << "}; // Sentinel\n\n"; +} + void CppGenerator::writeClassRegister(QTextStream &s, const AbstractMetaClass *metaClass, - GeneratorContext &classContext, + const GeneratorContext &classContext, QTextStream &signatureStream) { const ComplexTypeEntry *classTypeEntry = metaClass->typeEntry(); - const AbstractMetaClass *enc = metaClass->enclosingClass(); - bool hasEnclosingClass = enc && enc->typeEntry()->codeGeneration() != TypeEntry::GenerateForSubclass; - QString enclosingObjectVariable = hasEnclosingClass ? QLatin1String("enclosingClass") : QLatin1String("module"); + const AbstractMetaClass *enc = metaClass->targetLangEnclosingClass(); + QString enclosingObjectVariable = enc ? QLatin1String("enclosingClass") : QLatin1String("module"); QString pyTypeName = cpythonTypeName(metaClass); QString initFunctionName = getInitFunctionName(classContext); // PYSIDE-510: Create a signatures string for the introspection feature. - s << "// The signatures string for the functions." << endl; - s << "// Multiple signatures have their index \"n:\" in front." << endl; - s << "static const char *" << initFunctionName << "_SignatureStrings[] = {" << endl; - QString line; - while (signatureStream.readLineInto(&line)) - s << INDENT << '"' << line << "\"," << endl; - s << INDENT << NULL_PTR << "}; // Sentinel" << endl << endl; + writeSignatureStrings(s, signatureStream, initFunctionName, "functions"); s << "void init_" << initFunctionName; - s << "(PyObject *" << enclosingObjectVariable << ")" << endl; - s << '{' << endl; + s << "(PyObject *" << enclosingObjectVariable << ")\n{\n"; // Multiple inheritance QString pyTypeBasesVariable = chopType(pyTypeName) + QLatin1String("_Type_bases"); const AbstractMetaClassList baseClasses = getBaseClasses(metaClass); if (metaClass->baseClassNames().size() > 1) { s << INDENT << "PyObject *" << pyTypeBasesVariable - << " = PyTuple_Pack(" << baseClasses.size() << ',' << endl; + << " = PyTuple_Pack(" << baseClasses.size() << ',' << Qt::endl; Indentation indent(INDENT); for (int i = 0, size = baseClasses.size(); i < size; ++i) { if (i) - s << "," << endl; + s << ",\n"; s << INDENT << "reinterpret_cast<PyObject *>(" << cpythonTypeNameExt(baseClasses.at(i)->typeEntry()) << ')'; } - s << ");" << endl << endl; + s << ");\n\n"; } // Create type and insert it in the module or enclosing class. const QString typePtr = QLatin1String("_") + chopType(pyTypeName) + QLatin1String("_Type"); - s << INDENT << typePtr << " = Shiboken::ObjectType::introduceWrapperType(" << endl; + s << INDENT << typePtr << " = Shiboken::ObjectType::introduceWrapperType(\n"; { Indentation indent(INDENT); // 1:enclosingObject - s << INDENT << enclosingObjectVariable << "," << endl; + s << INDENT << enclosingObjectVariable << ",\n"; QString typeName; if (!classContext.forSmartPointer()) typeName = metaClass->name(); @@ -4976,7 +5353,7 @@ void CppGenerator::writeClassRegister(QTextStream &s, typeName = classContext.preciseType()->cppSignature(); // 2:typeName - s << INDENT << "\"" << typeName << "\"," << endl; + s << INDENT << "\"" << typeName << "\",\n"; // 3:originalName s << INDENT << "\""; @@ -4988,46 +5365,52 @@ void CppGenerator::writeClassRegister(QTextStream &s, s << classContext.preciseType()->cppSignature(); } - s << "\"," << endl; + s << "\",\n"; // 4:typeSpec - s << INDENT << '&' << chopType(pyTypeName) << "_spec," << endl; - - // 5:signatureStrings - s << INDENT << initFunctionName << "_SignatureStrings," << endl; + s << INDENT << '&' << chopType(pyTypeName) << "_spec,\n"; - // 6:cppObjDtor + // 5:cppObjDtor s << INDENT; if (!metaClass->isNamespace() && !metaClass->hasPrivateDestructor()) { QString dtorClassName = metaClass->qualifiedCppName(); - if ((avoidProtectedHack() && metaClass->hasProtectedDestructor()) || classTypeEntry->isValue()) - dtorClassName = wrapperName(metaClass); + if (((avoidProtectedHack() && metaClass->hasProtectedDestructor()) || classTypeEntry->isValue()) + && classContext.useWrapper()) { + dtorClassName = classContext.wrapperName(); + } if (classContext.forSmartPointer()) - dtorClassName = wrapperName(classContext.preciseType()); + dtorClassName = classContext.smartPointerWrapperName(); - s << "&Shiboken::callCppDestructor< ::" << dtorClassName << " >," << endl; + s << "&Shiboken::callCppDestructor< ::" << dtorClassName << " >,\n"; } else { - s << "0," << endl; + s << "0,\n"; } - // 7:baseType - const auto base = metaClass->isNamespace() + // 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 << INDENT << "reinterpret_cast<SbkObjectType *>(" - << cpythonTypeNameExt(base->typeEntry()) << ")," << endl; + << cpythonTypeNameExt(base->typeEntry()) << "),\n"; } else { - s << INDENT << "0," << endl; + s << INDENT << "0,\n"; } - // 8:baseTypes + // 7:baseTypes if (metaClass->baseClassNames().size() > 1) - s << INDENT << pyTypeBasesVariable << ',' << endl; + s << INDENT << pyTypeBasesVariable << ',' << Qt::endl; else - s << INDENT << "0," << endl; + s << INDENT << "0,\n"; - // 9:wrapperflags + // 8:wrapperflags QByteArrayList wrapperFlags; - if (hasEnclosingClass) + if (enc) wrapperFlags.append(QByteArrayLiteral("Shiboken::ObjectType::WrapperFlags::InnerClass")); if (metaClass->deleteInMainThread()) wrapperFlags.append(QByteArrayLiteral("Shiboken::ObjectType::WrapperFlags::DeleteInMainThread")); @@ -5036,24 +5419,33 @@ void CppGenerator::writeClassRegister(QTextStream &s, else s << INDENT << wrapperFlags.join(" | "); } - s << INDENT << ");" << endl; - s << INDENT << endl; + s << INDENT << ");\n"; + s << INDENT << Qt::endl; + + s << INDENT << "auto pyType = reinterpret_cast<PyTypeObject *>(" << typePtr << ");\n"; + s << INDENT << "InitSignatureStrings(pyType, " << initFunctionName << "_SignatureStrings);\n"; + + if (usePySideExtensions()) + s << INDENT << "SbkObjectType_SetPropertyStrings(reinterpret_cast<PyTypeObject *>(" << typePtr << "), " + << chopType(pyTypeName) << "_PropertyStrings);\n"; if (!classContext.forSmartPointer()) - s << INDENT << cpythonTypeNameExt(classTypeEntry) << endl; + s << INDENT << cpythonTypeNameExt(classTypeEntry) << Qt::endl; else - s << INDENT << cpythonTypeNameExt(classContext.preciseType()) << endl; - s << INDENT << " = reinterpret_cast<PyTypeObject *>(" << pyTypeName << ");" << endl; - s << endl; + s << INDENT << cpythonTypeNameExt(classContext.preciseType()) << Qt::endl; + s << INDENT << " = reinterpret_cast<PyTypeObject *>(" << pyTypeName << ");\n"; + s << Qt::endl; // Register conversions for the type. writeConverterRegister(s, metaClass, classContext); - s << endl; + s << Qt::endl; // class inject-code target/beginning if (!classTypeEntry->codeSnips().isEmpty()) { - writeCodeSnips(s, classTypeEntry->codeSnips(), TypeSystem::CodeSnipPositionBeginning, TypeSystem::TargetLangCode, metaClass); - s << endl; + writeClassCodeSnips(s, classTypeEntry->codeSnips(), + TypeSystem::CodeSnipPositionBeginning, TypeSystem::TargetLangCode, + classContext); + s << Qt::endl; } // Fill multiple inheritance data, if needed. @@ -5061,27 +5453,25 @@ void CppGenerator::writeClassRegister(QTextStream &s, if (miClass) { s << INDENT << "MultipleInheritanceInitFunction func = "; if (miClass == metaClass) { - s << multipleInheritanceInitializerFunctionName(miClass) << ";" << endl; + s << multipleInheritanceInitializerFunctionName(miClass) << ";\n"; } else { s << "Shiboken::ObjectType::getMultipleInheritanceFunction(reinterpret_cast<SbkObjectType *>("; - s << cpythonTypeNameExt(miClass->typeEntry()) << "));" << endl; + s << cpythonTypeNameExt(miClass->typeEntry()) << "));\n"; } s << INDENT << "Shiboken::ObjectType::setMultipleInheritanceFunction("; - s << cpythonTypeName(metaClass) << ", func);" << endl; + s << cpythonTypeName(metaClass) << ", func);\n"; s << INDENT << "Shiboken::ObjectType::setCastFunction(" << cpythonTypeName(metaClass); - s << ", &" << cpythonSpecialCastFunctionName(metaClass) << ");" << endl; + s << ", &" << cpythonSpecialCastFunctionName(metaClass) << ");\n"; } // Set typediscovery struct or fill the struct of another one if (metaClass->isPolymorphic() && metaClass->baseClass()) { s << INDENT << "Shiboken::ObjectType::setTypeDiscoveryFunctionV2(" << cpythonTypeName(metaClass); - s << ", &" << cpythonBaseName(metaClass) << "_typeDiscovery);" << endl << endl; + s << ", &" << cpythonBaseName(metaClass) << "_typeDiscovery);\n\n"; } AbstractMetaEnumList classEnums = metaClass->enums(); - const AbstractMetaClassList &innerClasses = metaClass->innerClasses(); - for (AbstractMetaClass *innerClass : innerClasses) - lookForEnumsInClassesNotToBeGenerated(classEnums, innerClass); + metaClass->getEnumsFromInvisibleNamespacesToBeGenerated(&classEnums); ErrorCode errorCode(QString::fromLatin1("")); writeEnumsInitialization(s, classEnums); @@ -5097,33 +5487,40 @@ void CppGenerator::writeClassRegister(QTextStream &s, s << INDENT << QLatin1String("PyDict_SetItemString(reinterpret_cast<PyTypeObject *>(") + cpythonTypeName(metaClass) + QLatin1String(")->tp_dict, \""); s << field->name() << "\", "; writeToPythonConversion(s, field->type(), metaClass, metaClass->qualifiedCppName() + QLatin1String("::") + field->name()); - s << ");" << endl; + s << ");\n"; } - s << endl; + s << Qt::endl; // class inject-code target/end if (!classTypeEntry->codeSnips().isEmpty()) { - s << endl; - writeCodeSnips(s, classTypeEntry->codeSnips(), TypeSystem::CodeSnipPositionEnd, TypeSystem::TargetLangCode, metaClass); + s << Qt::endl; + writeClassCodeSnips(s, classTypeEntry->codeSnips(), + TypeSystem::CodeSnipPositionEnd, TypeSystem::TargetLangCode, + classContext); } if (usePySideExtensions()) { - if (avoidProtectedHack() && shouldGenerateCppWrapper(metaClass)) - s << INDENT << wrapperName(metaClass) << "::pysideInitQtMetaTypes();\n"; + if (avoidProtectedHack() && classContext.useWrapper()) + s << INDENT << classContext.wrapperName() << "::pysideInitQtMetaTypes();\n"; else writeInitQtMetaTypeFunctionBody(s, classContext); } if (usePySideExtensions() && metaClass->isQObject()) { - s << INDENT << "Shiboken::ObjectType::setSubTypeInitHook(" << pyTypeName << ", &PySide::initQObjectSubType);" << endl; + s << INDENT << "Shiboken::ObjectType::setSubTypeInitHook(" << pyTypeName << ", &PySide::initQObjectSubType);\n"; s << INDENT << "PySide::initDynamicMetaObject(" << pyTypeName << ", &::" << metaClass->qualifiedCppName() - << "::staticMetaObject, sizeof(::" << metaClass->qualifiedCppName() << "));" << endl; + << "::staticMetaObject, sizeof("; + if (shouldGenerateCppWrapper(metaClass)) + s << wrapperName(metaClass); + else + s << "::" << metaClass->qualifiedCppName(); + s << "));\n"; } - s << '}' << endl; + s << "}\n"; } -void CppGenerator::writeInitQtMetaTypeFunctionBody(QTextStream &s, GeneratorContext &context) const +void CppGenerator::writeInitQtMetaTypeFunctionBody(QTextStream &s, const GeneratorContext &context) const { const AbstractMetaClass *metaClass = context.metaClass(); // Gets all class name variants used on different possible scopes @@ -5170,20 +5567,19 @@ void CppGenerator::writeInitQtMetaTypeFunctionBody(QTextStream &s, GeneratorCont .arg(QFile::decodeName(__FILE__)).arg(__LINE__); continue; } - s << INDENT << "qRegisterMetaType< ::" << className << " >(\"" << name << "\");" << endl; + s << INDENT << "qRegisterMetaType< ::" << className << " >(\"" << name << "\");\n"; } } } - const AbstractMetaEnumList &enums = metaClass->enums(); - for (AbstractMetaEnum *metaEnum : enums) { + for (AbstractMetaEnum *metaEnum : metaClass->enums()) { if (!metaEnum->isPrivate() && !metaEnum->isAnonymous()) { for (const QString &name : qAsConst(nameVariants)) - s << INDENT << "qRegisterMetaType< ::" << metaEnum->typeEntry()->qualifiedCppName() << " >(\"" << name << "::" << metaEnum->name() << "\");" << endl; + s << INDENT << "qRegisterMetaType< ::" << metaEnum->typeEntry()->qualifiedCppName() << " >(\"" << name << "::" << metaEnum->name() << "\");\n"; if (metaEnum->typeEntry()->flags()) { QString n = metaEnum->typeEntry()->flags()->originalName(); - s << INDENT << "qRegisterMetaType< ::" << n << " >(\"" << n << "\");" << endl; + s << INDENT << "qRegisterMetaType< ::" << n << " >(\"" << n << "\");\n"; } } } @@ -5193,17 +5589,17 @@ void CppGenerator::writeTypeDiscoveryFunction(QTextStream &s, const AbstractMeta { QString polymorphicExpr = metaClass->typeEntry()->polymorphicIdValue(); - s << "static void *" << cpythonBaseName(metaClass) << "_typeDiscovery(void *cptr, SbkObjectType *instanceType)\n{" << endl; + s << "static void *" << cpythonBaseName(metaClass) << "_typeDiscovery(void *cptr, SbkObjectType *instanceType)\n{\n"; if (!polymorphicExpr.isEmpty()) { polymorphicExpr = polymorphicExpr.replace(QLatin1String("%1"), QLatin1String(" reinterpret_cast< ::") + metaClass->qualifiedCppName() + QLatin1String(" *>(cptr)")); - s << INDENT << " if (" << polymorphicExpr << ")" << endl; + s << INDENT << " if (" << polymorphicExpr << ")\n"; { Indentation indent(INDENT); - s << INDENT << "return cptr;" << endl; + s << INDENT << "return cptr;\n"; } } else if (metaClass->isPolymorphic()) { const AbstractMetaClassList &ancestors = getAllAncestors(metaClass); @@ -5212,10 +5608,10 @@ void CppGenerator::writeTypeDiscoveryFunction(QTextStream &s, const AbstractMeta continue; if (ancestor->isPolymorphic()) { s << INDENT << "if (instanceType == reinterpret_cast<SbkObjectType *>(Shiboken::SbkType< ::" - << ancestor->qualifiedCppName() << " >()))" << endl; + << ancestor->qualifiedCppName() << " >()))\n"; Indentation indent(INDENT); s << INDENT << "return dynamic_cast< ::" << metaClass->qualifiedCppName() - << " *>(reinterpret_cast< ::"<< ancestor->qualifiedCppName() << " *>(cptr));" << endl; + << " *>(reinterpret_cast< ::"<< ancestor->qualifiedCppName() << " *>(cptr));\n"; } else { qCWarning(lcShiboken).noquote().nospace() << metaClass->qualifiedCppName() << " inherits from a non polymorphic type (" @@ -5225,7 +5621,7 @@ void CppGenerator::writeTypeDiscoveryFunction(QTextStream &s, const AbstractMeta } } - s << INDENT << "return {};" << endl; + s << INDENT << "return {};\n"; s << "}\n\n"; } @@ -5235,168 +5631,250 @@ QString CppGenerator::writeSmartPointerGetterCast() + QLatin1String(SMART_POINTER_GETTER) + QLatin1Char(')'); } -void CppGenerator::writeSetattroFunction(QTextStream &s, GeneratorContext &context) +void CppGenerator::writeSetattroDefinition(QTextStream &s, const AbstractMetaClass *metaClass) const { + s << "static int " << ShibokenGenerator::cpythonSetattroFunctionName(metaClass) + << "(PyObject *self, PyObject *name, PyObject *value)\n{\n"; + if (wrapperDiagnostics()) { + s << INDENT << R"(std::cerr << __FUNCTION__ << ' ' << Shiboken::debugPyObject(name) + << ' ' << Shiboken::debugPyObject(value) << '\n';)" << '\n'; + } +} + +inline void CppGenerator::writeSetattroDefaultReturn(QTextStream &s) const +{ + s << INDENT << "return PyObject_GenericSetAttr(self, name, value);\n}\n\n"; +} + +void CppGenerator::writeSetattroFunction(QTextStream &s, AttroCheck attroCheck, + const GeneratorContext &context) +{ + Q_ASSERT(!context.forSmartPointer()); const AbstractMetaClass *metaClass = context.metaClass(); - s << "static int " << cpythonSetattroFunctionName(metaClass) << "(PyObject *self, PyObject *name, PyObject *value)" << endl; - s << '{' << endl; - if (usePySideExtensions()) { - s << INDENT << "Shiboken::AutoDecRef pp(reinterpret_cast<PyObject *>(PySide::Property::getObject(self, name)));" << endl; - s << INDENT << "if (!pp.isNull())" << endl; + writeSetattroDefinition(s, metaClass); + + // PYSIDE-1019: Switch tp_dict before doing tp_setattro. + if (usePySideExtensions()) + s << INDENT << "PySide::Feature::Select(self);\n"; + + // PYSIDE-803: Detect duck-punching; clear cache if a method is set. + if (attroCheck.testFlag(AttroCheckFlag::SetattroMethodOverride) + && context.useWrapper()) { + s << INDENT << "if (value && PyCallable_Check(value)) {\n"; + s << INDENT << " auto plain_inst = " << cpythonWrapperCPtr(metaClass, QLatin1String("self")) << ";\n"; + s << INDENT << " auto inst = dynamic_cast<" << context.wrapperName() << " *>(plain_inst);\n"; + s << INDENT << " if (inst)\n"; + s << INDENT << " inst->resetPyMethodCache();\n"; + s << INDENT << "}\n"; + } + if (attroCheck.testFlag(AttroCheckFlag::SetattroQObject)) { + s << INDENT << "Shiboken::AutoDecRef pp(reinterpret_cast<PyObject *>(PySide::Property::getObject(self, name)));\n"; + s << INDENT << "if (!pp.isNull())\n"; Indentation indent(INDENT); - s << INDENT << "return PySide::Property::setValue(reinterpret_cast<PySideProperty *>(pp.object()), self, value);" << endl; + s << INDENT << "return PySide::Property::setValue(reinterpret_cast<PySideProperty *>(pp.object()), self, value);\n"; } - if (context.forSmartPointer()) { - s << INDENT << "// Try to find the 'name' attribute, by retrieving the PyObject for the corresponding C++ object held by the smart pointer." << endl; - s << INDENT << "PyObject *rawObj = PyObject_CallMethod(self, " - << writeSmartPointerGetterCast() << ", 0);" << endl; - s << INDENT << "if (rawObj) {" << endl; + if (attroCheck.testFlag(AttroCheckFlag::SetattroUser)) { + auto func = AbstractMetaClass::queryFirstFunction(metaClass->functions(), + AbstractMetaClass::SetAttroFunction); + Q_ASSERT(func); + s << INDENT << "{\n"; { Indentation indent(INDENT); - s << INDENT << "int hasAttribute = PyObject_HasAttr(rawObj, name);" << endl; - s << INDENT << "if (hasAttribute) {" << endl; - { - Indentation indent(INDENT); - s << INDENT << "return PyObject_GenericSetAttr(rawObj, name, value);" << endl; - } - s << INDENT << '}' << endl; - s << INDENT << "Py_DECREF(rawObj);" << endl; + s << INDENT << "auto " << CPP_SELF_VAR << " = " + << cpythonWrapperCPtr(metaClass, QLatin1String("self")) << ";\n"; + writeClassCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionAny, + TypeSystem::TargetLangCode, context); } - s << INDENT << '}' << endl; + s << INDENT << "}\n"; + } + + writeSetattroDefaultReturn(s); +} +void CppGenerator::writeSmartPointerSetattroFunction(QTextStream &s, const GeneratorContext &context) +{ + Q_ASSERT(context.forSmartPointer()); + writeSetattroDefinition(s, context.metaClass()); + s << INDENT << "// Try to find the 'name' attribute, by retrieving the PyObject for the corresponding C++ object held by the smart pointer.\n"; + s << INDENT << "PyObject *rawObj = PyObject_CallMethod(self, " + << writeSmartPointerGetterCast() << ", 0);\n"; + s << INDENT << "if (rawObj) {\n"; + { + Indentation indent(INDENT); + s << INDENT << "int hasAttribute = PyObject_HasAttr(rawObj, name);\n"; + s << INDENT << "if (hasAttribute) {\n"; + { + Indentation indent(INDENT); + s << INDENT << "return PyObject_GenericSetAttr(rawObj, name, value);\n"; + } + s << INDENT << "}\n"; + s << INDENT << "Py_DECREF(rawObj);\n"; } + s << INDENT << "}\n"; + writeSetattroDefaultReturn(s); +} - s << INDENT << "return PyObject_GenericSetAttr(self, name, value);" << endl; - s << '}' << endl; +void CppGenerator::writeGetattroDefinition(QTextStream &s, const AbstractMetaClass *metaClass) +{ + s << "static PyObject *" << cpythonGetattroFunctionName(metaClass) + << "(PyObject *self, PyObject *name)\n{\n"; } -static inline QString qObjectClassName() { return QStringLiteral("QObject"); } -static inline QString qMetaObjectClassName() { return QStringLiteral("QMetaObject"); } +QString CppGenerator::qObjectGetAttroFunction() const +{ + static QString result; + if (result.isEmpty()) { + AbstractMetaClass *qobjectClass = AbstractMetaClass::findClass(classes(), qObjectT()); + Q_ASSERT(qobjectClass); + result = QLatin1String("PySide::getMetaDataFromQObject(") + + cpythonWrapperCPtr(qobjectClass, QLatin1String("self")) + + QLatin1String(", self, name)"); + } + return result; +} -void CppGenerator::writeGetattroFunction(QTextStream &s, GeneratorContext &context) +void CppGenerator::writeGetattroFunction(QTextStream &s, AttroCheck attroCheck, + const GeneratorContext &context) { + Q_ASSERT(!context.forSmartPointer()); const AbstractMetaClass *metaClass = context.metaClass(); - s << "static PyObject *" << cpythonGetattroFunctionName(metaClass) << "(PyObject *self, PyObject *name)" << endl; - s << '{' << endl; + writeGetattroDefinition(s, metaClass); - QString getattrFunc; - if (usePySideExtensions() && metaClass->isQObject()) { - AbstractMetaClass *qobjectClass = AbstractMetaClass::findClass(classes(), qObjectClassName()); - QTextStream(&getattrFunc) << "PySide::getMetaDataFromQObject(" - << cpythonWrapperCPtr(qobjectClass, QLatin1String("self")) - << ", self, name)"; - } else { - getattrFunc = QLatin1String("PyObject_GenericGetAttr(") + QLatin1String("self") - + QLatin1String(", name)"); - } + // PYSIDE-1019: Switch tp_dict before doing tp_getattro. + if (usePySideExtensions()) + s << INDENT << "PySide::Feature::Select(self);\n"; - if (classNeedsGetattroFunction(metaClass)) { - s << INDENT << "if (self) {" << endl; + const QString getattrFunc = usePySideExtensions() && metaClass->isQObject() + ? qObjectGetAttroFunction() : QLatin1String("PyObject_GenericGetAttr(self, name)"); + + if (attroCheck.testFlag(AttroCheckFlag::GetattroOverloads)) { + s << INDENT << "// Search the method in the instance dict\n"; + s << INDENT << "if (auto ob_dict = reinterpret_cast<SbkObject *>(self)->ob_dict) {\n"; { Indentation indent(INDENT); - s << INDENT << "// Search the method in the instance dict" << endl; - s << INDENT << "if (reinterpret_cast<SbkObject *>(self)->ob_dict) {" << endl; + s << INDENT << "if (auto meth = PyDict_GetItem(ob_dict, name)) {\n"; { Indentation indent(INDENT); - s << INDENT << "PyObject *meth = PyDict_GetItem(reinterpret_cast<SbkObject *>(self)->ob_dict, name);" << endl; - s << INDENT << "if (meth) {" << endl; - { - Indentation indent(INDENT); - s << INDENT << "Py_INCREF(meth);" << endl; - s << INDENT << "return meth;" << endl; - } - s << INDENT << '}' << endl; + s << INDENT << "Py_INCREF(meth);\n"; + s << INDENT << "return meth;\n"; } - s << INDENT << '}' << endl; - s << INDENT << "// Search the method in the type dict" << endl; - s << INDENT << "if (Shiboken::Object::isUserType(self)) {" << endl; + s << INDENT << "}\n"; + } + s << INDENT << "}\n"; + s << INDENT << "// Search the method in the type dict\n"; + s << INDENT << "if (Shiboken::Object::isUserType(self)) {\n"; + { + Indentation indent(INDENT); + // PYSIDE-772: Perform optimized name mangling. + s << INDENT << "Shiboken::AutoDecRef tmp(_Pep_PrivateMangle(self, name));\n"; + s << INDENT << "if (auto meth = PyDict_GetItem(Py_TYPE(self)->tp_dict, tmp))\n"; { Indentation indent(INDENT); - // PYSIDE-772: Perform optimized name mangling. - s << INDENT << "Shiboken::AutoDecRef tmp(_Pep_PrivateMangle(self, name));" << endl; - s << INDENT << "PyObject *meth = PyDict_GetItem(Py_TYPE(self)->tp_dict, tmp);" << endl; - s << INDENT << "if (meth)" << endl; - { - Indentation indent(INDENT); - s << INDENT << "return PyFunction_Check(meth) ? SBK_PyMethod_New(meth, self) : " << getattrFunc << ';' << endl; - } + s << INDENT << "return PyFunction_Check(meth) ? SBK_PyMethod_New(meth, self) : " << getattrFunc << ";\n"; } - s << INDENT << '}' << endl; + } + s << INDENT << "}\n"; - const AbstractMetaFunctionList &funcs = getMethodsWithBothStaticAndNonStaticMethods(metaClass); - for (const AbstractMetaFunction *func : funcs) { - QString defName = cpythonMethodDefinitionName(func); - s << INDENT << "static PyMethodDef non_static_" << defName << " = {" << endl; - { - Indentation indent(INDENT); - s << INDENT << defName << ".ml_name," << endl; - s << INDENT << defName << ".ml_meth," << endl; - s << INDENT << defName << ".ml_flags & (~METH_STATIC)," << endl; - s << INDENT << defName << ".ml_doc," << endl; - } - s << INDENT << "};" << endl; - s << INDENT << "if (Shiboken::String::compare(name, \"" << func->name() << "\") == 0)" << endl; + const AbstractMetaFunctionList &funcs = getMethodsWithBothStaticAndNonStaticMethods(metaClass); + for (const AbstractMetaFunction *func : funcs) { + QString defName = cpythonMethodDefinitionName(func); + s << INDENT << "static PyMethodDef non_static_" << defName << " = {\n"; + { Indentation indent(INDENT); - s << INDENT << "return PyCFunction_NewEx(&non_static_" << defName << ", self, 0);" << endl; + s << INDENT << defName << ".ml_name,\n"; + s << INDENT << defName << ".ml_meth,\n"; + s << INDENT << defName << ".ml_flags & (~METH_STATIC),\n"; + s << INDENT << defName << ".ml_doc,\n"; } + s << INDENT << "};\n"; + s << INDENT << "if (Shiboken::String::compare(name, \"" << func->name() << "\") == 0)\n"; + Indentation indent(INDENT); + s << INDENT << "return PyCFunction_NewEx(&non_static_" << defName << ", self, 0);\n"; } - s << INDENT << '}' << endl; } - if (context.forSmartPointer()) { - s << INDENT << "PyObject *tmp = " << getattrFunc << ';' << endl; - s << INDENT << "if (tmp) {" << endl; + if (attroCheck.testFlag(AttroCheckFlag::GetattroUser)) { + auto func = AbstractMetaClass::queryFirstFunction(metaClass->functions(), + AbstractMetaClass::GetAttroFunction); + Q_ASSERT(func); + s << INDENT << "{\n"; { Indentation indent(INDENT); - s << INDENT << "return tmp;" << endl; + s << INDENT << "auto " << CPP_SELF_VAR << " = " + << cpythonWrapperCPtr(metaClass, QLatin1String("self")) << ";\n"; + writeClassCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionAny, + TypeSystem::TargetLangCode, context); } - s << INDENT << "} else {" << endl; + s << INDENT << "}\n"; + } + + s << INDENT << "return " << getattrFunc << ";\n}\n\n"; +} + +void CppGenerator::writeSmartPointerGetattroFunction(QTextStream &s, const GeneratorContext &context) +{ + Q_ASSERT(context.forSmartPointer()); + const AbstractMetaClass *metaClass = context.metaClass(); + writeGetattroDefinition(s, metaClass); + s << INDENT << "PyObject *tmp = PyObject_GenericGetAttr(self, name);\n"; + s << INDENT << "if (tmp)\n"; + { + Indentation indent(INDENT); + s << INDENT << "return tmp;\n"; + } + s << INDENT << "if (!PyErr_ExceptionMatches(PyExc_AttributeError))\n"; + { + Indentation indent(INDENT); + s << INDENT << "return nullptr;\n"; + } + s << INDENT << "PyErr_Clear();\n"; + + // This generates the code which dispatches access to member functions + // and fields from the smart pointer to its pointee. + s << INDENT << "// Try to find the 'name' attribute, by retrieving the PyObject for " + "the corresponding C++ object held by the smart pointer.\n"; + s << INDENT << "if (auto rawObj = PyObject_CallMethod(self, " + << writeSmartPointerGetterCast() << ", 0)) {\n"; + { + Indentation indent(INDENT); + s << INDENT << "if (auto attribute = PyObject_GetAttr(rawObj, name))\n"; { Indentation indent(INDENT); - s << INDENT << "if (!PyErr_ExceptionMatches(PyExc_AttributeError)) return nullptr;" << endl; - s << INDENT << "PyErr_Clear();" << endl; - - s << INDENT << "// Try to find the 'name' attribute, by retrieving the PyObject for " - "the corresponding C++ object held by the smart pointer." << endl; - s << INDENT << "PyObject *rawObj = PyObject_CallMethod(self, " - << writeSmartPointerGetterCast() << ", 0);" << endl; - s << INDENT << "if (rawObj) {" << endl; - { - Indentation indent(INDENT); - s << INDENT << "PyObject *attribute = PyObject_GetAttr(rawObj, name);" << endl; - s << INDENT << "if (attribute) {" << endl; - { - Indentation indent(INDENT); - s << INDENT << "tmp = attribute;" << endl; - } - s << INDENT << '}' << endl; - s << INDENT << "Py_DECREF(rawObj);" << endl; - } - s << INDENT << '}' << endl; - s << INDENT << "if (!tmp) {" << endl; - { - Indentation indent(INDENT); - s << INDENT << "PyTypeObject *tp = Py_TYPE(self);" << endl; - s << INDENT << "PyErr_Format(PyExc_AttributeError," << endl; - s << INDENT << " \"'%.50s' object has no attribute '%.400s'\"," << endl; - s << INDENT << " tp->tp_name, Shiboken::String::toCString(name));" << endl; - s << INDENT << "return nullptr;" << endl; - } - s << INDENT << "} else {" << endl; - { - Indentation indent(INDENT); - s << INDENT << "return tmp;" << endl; - } - s << INDENT << '}' << endl; - + s << INDENT << "tmp = attribute;\n"; } - s << INDENT << '}' << endl; - + s << INDENT << "Py_DECREF(rawObj);\n"; + } + s << INDENT << "}\n"; + s << INDENT << "if (!tmp) {\n"; + { + Indentation indent(INDENT); + s << INDENT << "PyTypeObject *tp = Py_TYPE(self);\n"; + s << INDENT << "PyErr_Format(PyExc_AttributeError,\n"; + s << INDENT << " \"'%.50s' object has no attribute '%.400s'\",\n"; + s << INDENT << " tp->tp_name, Shiboken::String::toCString(name));\n"; + } + s << INDENT << "}\n"; + s << INDENT << "return tmp;\n}\n\n"; +} + +// Write declaration and invocation of the init function for the module init +// function. +void CppGenerator::writeInitFunc(QTextStream &declStr, QTextStream &callStr, + const Indentor &indent, const QString &initFunctionName, + const TypeEntry *enclosingEntry) +{ + const bool hasParent = + enclosingEntry && enclosingEntry->type() != TypeEntry::TypeSystemType; + declStr << "void init_" << initFunctionName << "(PyObject *" + << (hasParent ? "enclosingClass" : "module") << ");\n"; + callStr << indent << "init_" << initFunctionName; + if (hasParent) { + callStr << "(reinterpret_cast<PyTypeObject *>(" + << cpythonTypeNameExt(enclosingEntry) << ")->tp_dict);\n"; } else { - s << INDENT << "return " << getattrFunc << ';' << endl; + callStr << "(module);\n"; } - s << '}' << endl; } bool CppGenerator::finishGeneration() @@ -5415,7 +5893,7 @@ bool CppGenerator::finishGeneration() QString signaturesString; QTextStream signatureStream(&signaturesString); - Indentation indent(INDENT); + Indentation indentation(INDENT); const auto functionGroups = getGlobalFunctionGroups(); for (auto it = functionGroups.cbegin(), end = functionGroups.cend(); it != end; ++it) { @@ -5442,8 +5920,8 @@ bool CppGenerator::finishGeneration() //We need move QMetaObject register before QObject Dependencies additionalDependencies; const AbstractMetaClassList &allClasses = classes(); - if (auto qObjectClass = AbstractMetaClass::findClass(allClasses, qObjectClassName())) { - if (auto qMetaObjectClass = AbstractMetaClass::findClass(allClasses, qMetaObjectClassName())) { + if (auto qObjectClass = AbstractMetaClass::findClass(allClasses, qObjectT())) { + if (auto qMetaObjectClass = AbstractMetaClass::findClass(allClasses, qMetaObjectT())) { Dependency dependency; dependency.parent = qMetaObjectClass; dependency.child = qObjectClass; @@ -5453,33 +5931,20 @@ bool CppGenerator::finishGeneration() const AbstractMetaClassList lst = classesTopologicalSorted(additionalDependencies); for (const AbstractMetaClass *cls : lst){ - if (!shouldGenerate(cls)) - continue; - - const QString initFunctionName = QLatin1String("init_") + getSimpleClassInitFunctionName(cls); - - s_classInitDecl << "void " << initFunctionName << "(PyObject *module);" << endl; - - s_classPythonDefines << INDENT << initFunctionName; - if (cls->enclosingClass() - && (cls->enclosingClass()->typeEntry()->codeGeneration() != TypeEntry::GenerateForSubclass)) { - s_classPythonDefines << "(reinterpret_cast<PyTypeObject *>(" - << cpythonTypeNameExt(cls->enclosingClass()->typeEntry()) << ")->tp_dict);"; - } else { - s_classPythonDefines << "(module);"; + if (shouldGenerate(cls)) { + writeInitFunc(s_classInitDecl, s_classPythonDefines, INDENT, + getSimpleClassInitFunctionName(cls), + cls->typeEntry()->targetLangEnclosingEntry()); } - s_classPythonDefines << endl; } // Initialize smart pointer types. const QVector<const AbstractMetaType *> &smartPtrs = instantiatedSmartPointers(); for (const AbstractMetaType *metaType : smartPtrs) { - GeneratorContext context(nullptr, metaType, true); - QString initFunctionName = getInitFunctionName(context); - s_classInitDecl << "void init_" << initFunctionName << "(PyObject *module);" << endl; - QString defineStr = QLatin1String("init_") + initFunctionName; - defineStr += QLatin1String("(module);"); - s_classPythonDefines << INDENT << defineStr << endl; + GeneratorContext context = contextForSmartPointer(nullptr, metaType); + writeInitFunc(s_classInitDecl, s_classPythonDefines, INDENT, + getInitFunctionName(context), + metaType->typeEntry()->targetLangEnclosingEntry()); } QString moduleFileName(outputDirectory() + QLatin1Char('/') + subDirectoryForPackage(packageName())); @@ -5492,135 +5957,126 @@ bool CppGenerator::finishGeneration() QTextStream &s = file.stream; // write license comment - s << licenseComment() << endl; + s << licenseComment() << Qt::endl; - s << "#include <sbkpython.h>" << endl; - s << "#include <shiboken.h>" << endl; - s << "#include <algorithm>" << endl; - s << "#include <signature.h>" << endl; + s << "#include <sbkpython.h>\n"; + s << "#include <shiboken.h>\n"; + s << "#include <algorithm>\n"; + s << "#include <signature.h>\n"; if (usePySideExtensions()) { s << includeQDebug; - s << "#include <pyside.h>" << endl; - s << "#include <qapp_macro.h>" << endl; + s << "#include <pyside.h>\n"; + s << "#include <pysideqenum.h>\n"; + s << "#include <feature_select.h>\n"; } - s << "#include \"" << getModuleHeaderFileName() << '"' << endl << endl; + s << "#include \"" << getModuleHeaderFileName() << '"' << Qt::endl << Qt::endl; for (const Include &include : qAsConst(includes)) s << include; - s << endl; + s << Qt::endl; // Global enums AbstractMetaEnumList globalEnums = this->globalEnums(); - const AbstractMetaClassList &classList = classes(); - for (const AbstractMetaClass *metaClass : classList) { - const AbstractMetaClass *encClass = metaClass->enclosingClass(); - if (encClass && encClass->typeEntry()->codeGeneration() != TypeEntry::GenerateForSubclass) - continue; - lookForEnumsInClassesNotToBeGenerated(globalEnums, metaClass); - } + for (const AbstractMetaClass *nsp : invisibleTopNamespaces()) + nsp->getEnumsToBeGenerated(&globalEnums); TypeDatabase *typeDb = TypeDatabase::instance(); const TypeSystemTypeEntry *moduleEntry = typeDb->defaultTypeSystemType(); Q_ASSERT(moduleEntry); //Extra includes - s << endl << "// Extra includes" << endl; + s << Qt::endl << "// Extra includes\n"; QVector<Include> extraIncludes = moduleEntry->extraIncludes(); for (AbstractMetaEnum *cppEnum : qAsConst(globalEnums)) extraIncludes.append(cppEnum->typeEntry()->extraIncludes()); std::sort(extraIncludes.begin(), extraIncludes.end()); for (const Include &inc : qAsConst(extraIncludes)) s << inc; - s << endl; + s << Qt::endl; - s << "// Current module's type array." << endl; - s << "PyTypeObject **" << cppApiVariableName() << " = nullptr;" << endl; + s << "// Current module's type array.\n"; + s << "PyTypeObject **" << cppApiVariableName() << " = nullptr;\n"; - s << "// Current module's PyObject pointer." << endl; - s << "PyObject *" << pythonModuleObjectName() << " = nullptr;" << endl; + s << "// Current module's PyObject pointer.\n"; + s << "PyObject *" << pythonModuleObjectName() << " = nullptr;\n"; - s << "// Current module's converter array." << endl; - s << "SbkConverter **" << convertersVariableName() << " = nullptr;" << endl; + s << "// Current module's converter array.\n"; + s << "SbkConverter **" << convertersVariableName() << " = nullptr;\n"; const CodeSnipList snips = moduleEntry->codeSnips(); // module inject-code native/beginning - if (!snips.isEmpty()) { + if (!snips.isEmpty()) writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::NativeCode); - s << endl; - } // cleanup staticMetaObject attribute if (usePySideExtensions()) { - s << "void cleanTypesAttributes(void) {" << endl; - s << INDENT << "if (PY_VERSION_HEX >= 0x03000000 && PY_VERSION_HEX < 0x03060000)" << endl; - s << INDENT << " return; // PYSIDE-953: testbinding crashes in Python 3.5 when hasattr touches types!" << endl; - s << INDENT << "for (int i = 0, imax = SBK_" << moduleName() << "_IDX_COUNT; i < imax; i++) {" << endl; - { - Indentation indentation(INDENT); - s << INDENT << "PyObject *pyType = reinterpret_cast<PyObject *>(" << cppApiVariableName() << "[i]);" << endl; - s << INDENT << "Shiboken::AutoDecRef attrName(Py_BuildValue(\"s\", \"staticMetaObject\"));" << endl; - s << INDENT << "if (pyType && PyObject_HasAttr(pyType, attrName))"<< endl; - { - Indentation indentation(INDENT); - s << INDENT << "PyObject_SetAttr(pyType, attrName, Py_None);" << endl; - } - } - s << INDENT << "}" << endl; - s << "}" << endl; + s << "void cleanTypesAttributes(void) {\n"; + s << INDENT << "if (PY_VERSION_HEX >= 0x03000000 && PY_VERSION_HEX < 0x03060000)\n"; + s << INDENT << " return; // PYSIDE-953: testbinding crashes in Python 3.5 when hasattr touches types!\n"; + s << INDENT << "for (int i = 0, imax = SBK_" << moduleName() + << "_IDX_COUNT; i < imax; i++) {\n" << indent(INDENT) + << INDENT << "PyObject *pyType = reinterpret_cast<PyObject *>(" << cppApiVariableName() << "[i]);\n" + << INDENT << "Shiboken::AutoDecRef attrName(Py_BuildValue(\"s\", \"staticMetaObject\"));\n" + << INDENT << "if (pyType && PyObject_HasAttr(pyType, attrName))\n" << indent(INDENT) + << INDENT << "PyObject_SetAttr(pyType, attrName, Py_None);\n" << outdent(INDENT) + << outdent(INDENT) << INDENT << "}\n" << "}\n"; } s << "// Global functions "; - s << "------------------------------------------------------------" << endl; - s << globalFunctionImpl << endl; + s << "------------------------------------------------------------\n"; + s << globalFunctionImpl << Qt::endl; - s << "static PyMethodDef " << moduleName() << "_methods[] = {" << endl; + s << "static PyMethodDef " << moduleName() << "_methods[] = {\n"; s << globalFunctionDecl; - s << INDENT << "{0} // Sentinel" << endl << "};" << endl << endl; + s << INDENT << "{0} // Sentinel\n" << "};\n\n"; s << "// Classes initialization functions "; - s << "------------------------------------------------------------" << endl; - s << classInitDecl << endl; + s << "------------------------------------------------------------\n"; + s << classInitDecl << Qt::endl; if (!globalEnums.isEmpty()) { QString converterImpl; QTextStream convImpl(&converterImpl); s << "// Enum definitions "; - s << "------------------------------------------------------------" << endl; + s << "------------------------------------------------------------\n"; for (const AbstractMetaEnum *cppEnum : qAsConst(globalEnums)) { if (cppEnum->isAnonymous() || cppEnum->isPrivate()) continue; writeEnumConverterFunctions(s, cppEnum); - s << endl; + s << Qt::endl; } if (!converterImpl.isEmpty()) { s << "// Enum converters "; - s << "------------------------------------------------------------" << endl; - s << "namespace Shiboken" << endl << '{' << endl; - s << converterImpl << endl; - s << "} // namespace Shiboken" << endl << endl; + s << "------------------------------------------------------------\n"; + s << "namespace Shiboken\n{\n"; + s << converterImpl << Qt::endl; + s << "} // namespace Shiboken\n\n"; } + + writeFlagsNumberMethodsDefinitions(s, globalEnums); + s << '\n'; } const QStringList &requiredModules = typeDb->requiredTargetImports(); if (!requiredModules.isEmpty()) - s << "// Required modules' type and converter arrays." << endl; + s << "// Required modules' type and converter arrays.\n"; for (const QString &requiredModule : requiredModules) { - s << "PyTypeObject **" << cppApiVariableName(requiredModule) << ';' << endl; - s << "SbkConverter **" << convertersVariableName(requiredModule) << ';' << endl; + s << "PyTypeObject **" << cppApiVariableName(requiredModule) << ";\n"; + s << "SbkConverter **" << convertersVariableName(requiredModule) << ";\n"; } - s << endl; + s << Qt::endl; s << "// Module initialization "; - s << "------------------------------------------------------------" << endl; + s << "------------------------------------------------------------\n"; ExtendedConverterData extendedConverters = getExtendedConverters(); if (!extendedConverters.isEmpty()) { - s << endl << "// Extended Converters." << endl << endl; + s << Qt::endl << "// Extended Converters.\n\n"; for (ExtendedConverterData::const_iterator it = extendedConverters.cbegin(), end = extendedConverters.cend(); it != end; ++it) { const TypeEntry *externalType = it.key(); - s << "// Extended implicit conversions for " << externalType->qualifiedTargetLangName() << '.' << endl; + s << "// Extended implicit conversions for " << externalType->qualifiedTargetLangName() << '.' << Qt::endl; for (const AbstractMetaClass *sourceClass : it.value()) { AbstractMetaType *sourceType = buildAbstractMetaTypeFromAbstractMetaClass(sourceClass); AbstractMetaType *targetType = buildAbstractMetaTypeFromTypeEntry(externalType); @@ -5631,135 +6087,134 @@ bool CppGenerator::finishGeneration() const QVector<const CustomConversion *> &typeConversions = getPrimitiveCustomConversions(); if (!typeConversions.isEmpty()) { - s << endl << "// Primitive Type converters." << endl << endl; + s << Qt::endl << "// Primitive Type converters.\n\n"; for (const CustomConversion *conversion : typeConversions) { - s << "// C++ to Python conversion for type '" << conversion->ownerType()->qualifiedCppName() << "'." << endl; + s << "// C++ to Python conversion for type '" << conversion->ownerType()->qualifiedCppName() << "'.\n"; writeCppToPythonFunction(s, conversion); writeCustomConverterFunctions(s, conversion); } - s << endl; + s << Qt::endl; } const QVector<const AbstractMetaType *> &containers = instantiatedContainers(); if (!containers.isEmpty()) { - s << "// Container Type converters." << endl << endl; + s << "// Container Type converters.\n\n"; for (const AbstractMetaType *container : containers) { - s << "// C++ to Python conversion for type '" << container->cppSignature() << "'." << endl; + s << "// C++ to Python conversion for type '" << container->cppSignature() << "'.\n"; writeContainerConverterFunctions(s, container); } - s << endl; - } - - s << "#if defined _WIN32 || defined __CYGWIN__" << endl; - s << " #define SBK_EXPORT_MODULE __declspec(dllexport)" << endl; - s << "#elif __GNUC__ >= 4" << endl; - s << " #define SBK_EXPORT_MODULE __attribute__ ((visibility(\"default\")))" << endl; - s << "#else" << endl; - s << " #define SBK_EXPORT_MODULE" << endl; - s << "#endif" << endl << endl; - - s << "#ifdef IS_PY3K" << endl; - s << "static struct PyModuleDef moduledef = {" << endl; - s << " /* m_base */ PyModuleDef_HEAD_INIT," << endl; - s << " /* m_name */ \"" << moduleName() << "\"," << endl; - s << " /* m_doc */ nullptr," << endl; - s << " /* m_size */ -1," << endl; - s << " /* m_methods */ " << moduleName() << "_methods," << endl; - s << " /* m_reload */ nullptr," << endl; - s << " /* m_traverse */ nullptr," << endl; - s << " /* m_clear */ nullptr," << endl; - s << " /* m_free */ nullptr" << endl; - s << "};" << endl << endl; - s << "#endif" << endl << endl; + s << Qt::endl; + } + + // Implicit smart pointers conversions + const auto smartPointersList = instantiatedSmartPointers(); + if (!smartPointersList.isEmpty()) { + s << "// SmartPointers converters.\n\n"; + for (const AbstractMetaType *smartPointer : smartPointersList) { + s << "// C++ to Python conversion for type '" << smartPointer->cppSignature() << "'.\n"; + writeSmartPointerConverterFunctions(s, smartPointer); + } + s << Qt::endl; + } + + s << "#ifdef IS_PY3K\n"; + s << "static struct PyModuleDef moduledef = {\n"; + s << " /* m_base */ PyModuleDef_HEAD_INIT,\n"; + s << " /* m_name */ \"" << moduleName() << "\",\n"; + s << " /* m_doc */ nullptr,\n"; + s << " /* m_size */ -1,\n"; + s << " /* m_methods */ " << moduleName() << "_methods,\n"; + s << " /* m_reload */ nullptr,\n"; + s << " /* m_traverse */ nullptr,\n"; + s << " /* m_clear */ nullptr,\n"; + s << " /* m_free */ nullptr\n"; + s << "};\n\n"; + s << "#endif\n\n"; // PYSIDE-510: Create a signatures string for the introspection feature. - s << "// The signatures string for the global functions." << endl; - s << "// Multiple signatures have their index \"n:\" in front." << endl; - s << "static const char *" << moduleName() << "_SignatureStrings[] = {" << endl; - QString line; - while (signatureStream.readLineInto(&line)) - s << INDENT << '"' << line << "\"," << endl; - s << INDENT << NULL_PTR << "}; // Sentinel" << endl << endl; + writeSignatureStrings(s, signatureStream, moduleName(), "global functions"); - s << "SBK_MODULE_INIT_FUNCTION_BEGIN(" << moduleName() << ")" << endl; + s << "SBK_MODULE_INIT_FUNCTION_BEGIN(" << moduleName() << ")\n"; ErrorCode errorCode(QLatin1String("SBK_MODULE_INIT_ERROR")); // module inject-code target/beginning - if (!snips.isEmpty()) { + if (!snips.isEmpty()) writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::TargetLangCode); - s << endl; - } for (const QString &requiredModule : requiredModules) { - s << INDENT << "{" << endl; - { - Indentation indentation(INDENT); - s << INDENT << "Shiboken::AutoDecRef requiredModule(Shiboken::Module::import(\"" << requiredModule << "\"));" << endl; - s << INDENT << "if (requiredModule.isNull())" << endl; - { - Indentation indentation(INDENT); - s << INDENT << "return SBK_MODULE_INIT_ERROR;" << endl; - } - s << INDENT << cppApiVariableName(requiredModule) << " = Shiboken::Module::getTypes(requiredModule);" << endl; - s << INDENT << convertersVariableName(requiredModule) << " = Shiboken::Module::getTypeConverters(requiredModule);" << endl; - } - s << INDENT << "}" << endl << endl; + s << INDENT << "{\n" << indent(INDENT) + << INDENT << "Shiboken::AutoDecRef requiredModule(Shiboken::Module::import(\"" << requiredModule << "\"));\n" + << INDENT << "if (requiredModule.isNull())\n" << indent(INDENT) + << INDENT << "return SBK_MODULE_INIT_ERROR;\n" << outdent(INDENT) + << INDENT << cppApiVariableName(requiredModule) + << " = Shiboken::Module::getTypes(requiredModule);\n" + << INDENT << convertersVariableName(requiredModule) + << " = Shiboken::Module::getTypeConverters(requiredModule);\n" << outdent(INDENT) + << INDENT << "}\n\n"; } int maxTypeIndex = getMaxTypeIndex() + instantiatedSmartPointers().size(); if (maxTypeIndex) { - s << INDENT << "// Create an array of wrapper types for the current module." << endl; - s << INDENT << "static PyTypeObject *cppApi[SBK_" << moduleName() << "_IDX_COUNT];" << endl; - s << INDENT << cppApiVariableName() << " = cppApi;" << endl << endl; + s << INDENT << "// Create an array of wrapper types for the current module.\n"; + s << INDENT << "static PyTypeObject *cppApi[SBK_" << moduleName() << "_IDX_COUNT];\n"; + s << INDENT << cppApiVariableName() << " = cppApi;\n\n"; } - s << INDENT << "// Create an array of primitive type converters for the current module." << endl; - s << INDENT << "static SbkConverter *sbkConverters[SBK_" << moduleName() << "_CONVERTERS_IDX_COUNT" << "];" << endl; - s << INDENT << convertersVariableName() << " = sbkConverters;" << endl << endl; + s << INDENT << "// Create an array of primitive type converters for the current module.\n"; + s << INDENT << "static SbkConverter *sbkConverters[SBK_" << moduleName() << "_CONVERTERS_IDX_COUNT" << "];\n"; + s << INDENT << convertersVariableName() << " = sbkConverters;\n\n"; - s << "#ifdef IS_PY3K" << endl; - s << INDENT << "PyObject *module = Shiboken::Module::create(\"" << moduleName() << "\", &moduledef);" << endl; - s << "#else" << endl; + s << "#ifdef IS_PY3K\n"; + s << INDENT << "PyObject *module = Shiboken::Module::create(\"" << moduleName() << "\", &moduledef);\n"; + s << "#else\n"; s << INDENT << "PyObject *module = Shiboken::Module::create(\"" << moduleName() << "\", "; - s << moduleName() << "_methods);" << endl; - s << "#endif" << endl << endl; + s << moduleName() << "_methods);\n"; + s << "#endif\n\n"; - s << INDENT << "// Make module available from global scope" << endl; - s << INDENT << pythonModuleObjectName() << " = module;" << endl << endl; + s << INDENT << "// Make module available from global scope\n"; + s << INDENT << pythonModuleObjectName() << " = module;\n\n"; - //s << INDENT << "// Initialize converters for primitive types." << endl; - //s << INDENT << "initConverters();" << endl << endl; + //s << INDENT << "// Initialize converters for primitive types.\n"; + //s << INDENT << "initConverters();\n\n"; - s << INDENT << "// Initialize classes in the type system" << endl; + s << INDENT << "// Initialize classes in the type system\n"; s << classPythonDefines; if (!typeConversions.isEmpty()) { - s << endl; + s << Qt::endl; for (const CustomConversion *conversion : typeConversions) { writePrimitiveConverterInitialization(s, conversion); - s << endl; + s << Qt::endl; } } if (!containers.isEmpty()) { - s << endl; + s << Qt::endl; for (const AbstractMetaType *container : containers) { writeContainerConverterInitialization(s, container); - s << endl; + s << Qt::endl; + } + } + + if (!smartPointersList.isEmpty()) { + s << Qt::endl; + for (const AbstractMetaType *smartPointer : smartPointersList) { + writeSmartPointerConverterInitialization(s, smartPointer); + s << Qt::endl; } } if (!extendedConverters.isEmpty()) { - s << endl; + s << Qt::endl; for (ExtendedConverterData::const_iterator it = extendedConverters.cbegin(), end = extendedConverters.cend(); it != end; ++it) { writeExtendedConverterInitialization(s, it.key(), it.value()); - s << endl; + s << Qt::endl; } } writeEnumsInitialization(s, globalEnums); - s << INDENT << "// Register primitive types converters." << endl; + s << INDENT << "// Register primitive types converters.\n"; const PrimitiveTypeEntryList &primitiveTypeList = primitiveTypes(); for (const PrimitiveTypeEntry *pte : primitiveTypeList) { if (!pte->generateCode() || !pte->isCppPrimitive()) @@ -5768,59 +6223,48 @@ bool CppGenerator::finishGeneration() if (!referencedType) continue; QString converter = converterObject(referencedType); - QStringList cppSignature = pte->qualifiedCppName().split(QLatin1String("::"), QString::SkipEmptyParts); + QStringList cppSignature = pte->qualifiedCppName().split(QLatin1String("::"), Qt::SkipEmptyParts); while (!cppSignature.isEmpty()) { QString signature = cppSignature.join(QLatin1String("::")); - s << INDENT << "Shiboken::Conversions::registerConverterName(" << converter << ", \"" << signature << "\");" << endl; + s << INDENT << "Shiboken::Conversions::registerConverterName(" << converter << ", \"" << signature << "\");\n"; cppSignature.removeFirst(); } } - s << endl; + s << Qt::endl; if (maxTypeIndex) - s << INDENT << "Shiboken::Module::registerTypes(module, " << cppApiVariableName() << ");" << endl; - s << INDENT << "Shiboken::Module::registerTypeConverters(module, " << convertersVariableName() << ");" << endl; + s << INDENT << "Shiboken::Module::registerTypes(module, " << cppApiVariableName() << ");\n"; + s << INDENT << "Shiboken::Module::registerTypeConverters(module, " << convertersVariableName() << ");\n"; - s << endl << INDENT << "if (PyErr_Occurred()) {" << endl; - { - Indentation indentation(INDENT); - s << INDENT << "PyErr_Print();" << endl; - s << INDENT << "Py_FatalError(\"can't initialize module " << moduleName() << "\");" << endl; - } - s << INDENT << '}' << endl; + s << '\n' << INDENT << "if (PyErr_Occurred()) {\n" << indent(INDENT) + << INDENT << "PyErr_Print();\n" + << INDENT << "Py_FatalError(\"can't initialize module " << moduleName() << "\");\n" + << outdent(INDENT) << INDENT << "}\n"; // module inject-code target/end - if (!snips.isEmpty()) { + if (!snips.isEmpty()) writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::TargetLangCode); - s << endl; - } // module inject-code native/end - if (!snips.isEmpty()) { + if (!snips.isEmpty()) writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode); - s << endl; - } if (usePySideExtensions()) { for (AbstractMetaEnum *metaEnum : qAsConst(globalEnums)) if (!metaEnum->isAnonymous()) { - s << INDENT << "qRegisterMetaType< ::" << metaEnum->typeEntry()->qualifiedCppName() << " >(\"" << metaEnum->name() << "\");" << endl; + s << INDENT << "qRegisterMetaType< ::" << metaEnum->typeEntry()->qualifiedCppName() << " >(\"" << metaEnum->name() << "\");\n"; } // cleanup staticMetaObject attribute - s << INDENT << "PySide::registerCleanupFunction(cleanTypesAttributes);" << endl << endl; + s << INDENT << "PySide::registerCleanupFunction(cleanTypesAttributes);\n\n"; } // finish the rest of __signature__ initialization. s << INDENT << "FinishSignatureInitialization(module, " << moduleName() - << "_SignatureStrings);" << endl; + << "_SignatureStrings);\n"; - if (usePySideExtensions()) { - // initialize the qApp module. - s << INDENT << "NotifyModuleForQApp(module, qApp);" << endl; - } - s << endl; - s << "SBK_MODULE_INIT_FUNCTION_END" << endl; + s << Qt::endl; + s << "SBK_MODULE_INIT_FUNCTION_END\n"; return file.done() != FileOut::Failure; } @@ -5906,12 +6350,12 @@ void CppGenerator::writeParentChildManagement(QTextStream &s, const AbstractMeta writeReturnValueHeuristics(s, func); } -void CppGenerator::writeReturnValueHeuristics(QTextStream &s, const AbstractMetaFunction *func, const QString &self) +void CppGenerator::writeReturnValueHeuristics(QTextStream &s, const AbstractMetaFunction *func) { AbstractMetaType *type = func->type(); if (!useReturnValueHeuristic() || !func->ownerClass() - || !type + || type->isVoid() || func->isStatic() || func->isConstructor() || !func->typeReplaced(0).isEmpty()) { @@ -5921,126 +6365,136 @@ void CppGenerator::writeReturnValueHeuristics(QTextStream &s, const AbstractMeta ArgumentOwner argOwner = getArgumentOwner(func, ArgumentOwner::ReturnIndex); if (argOwner.action == ArgumentOwner::Invalid || argOwner.index != ArgumentOwner::ThisIndex) { if (isPointerToWrapperType(type)) - s << INDENT << "Shiboken::Object::setParent(self, " << PYTHON_RETURN_VAR << ");" << endl; + s << INDENT << "Shiboken::Object::setParent(self, " << PYTHON_RETURN_VAR << ");\n"; } } -void CppGenerator::writeHashFunction(QTextStream &s, GeneratorContext &context) +void CppGenerator::writeHashFunction(QTextStream &s, const GeneratorContext &context) { const AbstractMetaClass *metaClass = context.metaClass(); - s << "static Py_hash_t " << cpythonBaseName(metaClass) << "_HashFunc(PyObject *self) {" << endl; + const char hashType[] = "Py_hash_t"; + s << "static " << hashType << ' ' << cpythonBaseName(metaClass) + << "_HashFunc(PyObject *self) {\n"; writeCppSelfDefinition(s, context); - s << INDENT << "return " << metaClass->typeEntry()->hashFunction() << '('; - s << (isObjectType(metaClass) ? "" : "*") << CPP_SELF_VAR << ");" << endl; - s << '}' << endl << endl; + s << INDENT << "return " << hashType << '(' + << metaClass->typeEntry()->hashFunction() << '(' + << (isObjectType(metaClass) ? "" : "*") << CPP_SELF_VAR << "));\n"; + s<< "}\n\n"; } -void CppGenerator::writeStdListWrapperMethods(QTextStream &s, GeneratorContext &context) +void CppGenerator::writeDefaultSequenceMethods(QTextStream &s, const GeneratorContext &context) { const AbstractMetaClass *metaClass = context.metaClass(); ErrorCode errorCode(0); // __len__ - s << "Py_ssize_t " << cpythonBaseName(metaClass->typeEntry()) << "__len__(PyObject *self)" << endl; - s << '{' << endl; + s << "Py_ssize_t " << cpythonBaseName(metaClass->typeEntry()) + << "__len__(PyObject *self)\n{\n"; writeCppSelfDefinition(s, context); - s << INDENT << "return " << CPP_SELF_VAR << "->size();" << endl; - s << '}' << endl; + s << INDENT << "return " << CPP_SELF_VAR << "->size();\n"; + s << "}\n"; // __getitem__ - s << "PyObject *" << cpythonBaseName(metaClass->typeEntry()) << "__getitem__(PyObject *self, Py_ssize_t _i)" << endl; - s << '{' << endl; + s << "PyObject *" << cpythonBaseName(metaClass->typeEntry()) + << "__getitem__(PyObject *self, Py_ssize_t _i)\n{\n"; writeCppSelfDefinition(s, context); writeIndexError(s, QLatin1String("index out of bounds")); - s << INDENT << metaClass->qualifiedCppName() << "::iterator _item = " << CPP_SELF_VAR << "->begin();" << endl; - s << INDENT << "for (Py_ssize_t pos = 0; pos < _i; pos++) _item++;" << endl; + QString value; + s << INDENT << metaClass->qualifiedCppName() << "::const_iterator _item = " + << CPP_SELF_VAR << "->begin();\n" + << INDENT << "std::advance(_item, _i);\n"; - const AbstractMetaType *itemType = metaClass->templateBaseClassInstantiations().constFirst(); + const AbstractMetaTypeList &instantiations = metaClass->templateBaseClassInstantiations(); + if (instantiations.isEmpty()) { + qFatal("shiboken: %s: Internal error, no instantiations of \"%s\" were found.", + __FUNCTION__, qPrintable(metaClass->qualifiedCppName())); + } + const AbstractMetaType *itemType = instantiations.constFirst(); s << INDENT << "return "; writeToPythonConversion(s, itemType, metaClass, QLatin1String("*_item")); - s << ';' << endl; - s << '}' << endl; + s << ";\n"; + s << "}\n"; // __setitem__ ErrorCode errorCode2(-1); - s << "int " << cpythonBaseName(metaClass->typeEntry()) << "__setitem__(PyObject *self, Py_ssize_t _i, PyObject *pyArg)" << endl; - s << '{' << endl; + s << "int " << cpythonBaseName(metaClass->typeEntry()) + << "__setitem__(PyObject *self, Py_ssize_t _i, PyObject *pyArg)\n{\n"; writeCppSelfDefinition(s, context); writeIndexError(s, QLatin1String("list assignment index out of range")); - s << INDENT << "PythonToCppFunc " << PYTHON_TO_CPP_VAR << ';' << endl; + s << INDENT << "PythonToCppFunc " << PYTHON_TO_CPP_VAR << ";\n"; s << INDENT << "if (!"; writeTypeCheck(s, itemType, QLatin1String("pyArg"), isNumber(itemType->typeEntry())); - s << ") {" << endl; + s << ") {\n"; { Indentation indent(INDENT); s << INDENT << "PyErr_SetString(PyExc_TypeError, \"attributed value with wrong type, '"; - s << itemType->name() << "' or other convertible type expected\");" << endl; - s << INDENT << "return -1;" << endl; + s << itemType->name() << "' or other convertible type expected\");\n"; + s << INDENT << "return -1;\n"; } - s << INDENT << '}' << endl; + s << INDENT << "}\n"; writeArgumentConversion(s, itemType, QLatin1String("cppValue"), QLatin1String("pyArg"), metaClass); - s << INDENT << metaClass->qualifiedCppName() << "::iterator _item = " << CPP_SELF_VAR << "->begin();" << endl; - s << INDENT << "for (Py_ssize_t pos = 0; pos < _i; pos++) _item++;" << endl; - s << INDENT << "*_item = cppValue;" << endl; - s << INDENT << "return {};" << endl; - s << '}' << endl; + s << INDENT << metaClass->qualifiedCppName() << "::iterator _item = " + << CPP_SELF_VAR << "->begin();\n" + << INDENT << "std::advance(_item, _i);\n" + << INDENT << "*_item = cppValue;\n"; + + s << INDENT << "return {};\n"; + s << "}\n"; } void CppGenerator::writeIndexError(QTextStream &s, const QString &errorMsg) { - s << INDENT << "if (_i < 0 || _i >= (Py_ssize_t) " << CPP_SELF_VAR << "->size()) {" << endl; + s << INDENT << "if (_i < 0 || _i >= (Py_ssize_t) " << CPP_SELF_VAR << "->size()) {\n"; { Indentation indent(INDENT); - s << INDENT << "PyErr_SetString(PyExc_IndexError, \"" << errorMsg << "\");" << endl; - s << INDENT << returnStatement(m_currentErrorCode) << endl; + s << INDENT << "PyErr_SetString(PyExc_IndexError, \"" << errorMsg << "\");\n"; + s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl; } - s << INDENT << '}' << endl; + s << INDENT << "}\n"; } QString CppGenerator::writeReprFunction(QTextStream &s, - GeneratorContext &context, + const GeneratorContext &context, uint indirections) { const AbstractMetaClass *metaClass = context.metaClass(); QString funcName = cpythonBaseName(metaClass) + QLatin1String("__repr__"); - s << "extern \"C\"" << endl; - s << '{' << endl; - s << "static PyObject *" << funcName << "(PyObject *self)" << endl; - s << '{' << endl; + s << "extern \"C\"\n{\n"; + s << "static PyObject *" << funcName << "(PyObject *self)\n{\n"; writeCppSelfDefinition(s, context); - s << INDENT << "QBuffer buffer;" << endl; - s << INDENT << "buffer.open(QBuffer::ReadWrite);" << endl; - s << INDENT << "QDebug dbg(&buffer);" << endl; + s << INDENT << "QBuffer buffer;\n"; + s << INDENT << "buffer.open(QBuffer::ReadWrite);\n"; + s << INDENT << "QDebug dbg(&buffer);\n"; s << INDENT << "dbg << "; if (metaClass->typeEntry()->isValue() || indirections == 0) s << '*'; - s << CPP_SELF_VAR << ';' << endl; - s << INDENT << "buffer.close();" << endl; - s << INDENT << "QByteArray str = buffer.data();" << endl; - s << INDENT << "int idx = str.indexOf('(');" << endl; - s << INDENT << "if (idx >= 0)" << endl; + s << CPP_SELF_VAR << ";\n"; + s << INDENT << "buffer.close();\n"; + s << INDENT << "QByteArray str = buffer.data();\n"; + s << INDENT << "int idx = str.indexOf('(');\n"; + s << INDENT << "if (idx >= 0)\n"; { Indentation indent(INDENT); - s << INDENT << "str.replace(0, idx, Py_TYPE(self)->tp_name);" << endl; + s << INDENT << "str.replace(0, idx, Py_TYPE(self)->tp_name);\n"; } - s << INDENT << "str = str.trimmed();" << endl; - s << INDENT << "PyObject *mod = PyDict_GetItem(Py_TYPE(self)->tp_dict, Shiboken::PyMagicName::module());" << endl; + s << INDENT << "str = str.trimmed();\n"; + s << INDENT << "PyObject *mod = PyDict_GetItem(Py_TYPE(self)->tp_dict, 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 << INDENT << "if (mod && !strchr(str, '.'))" << endl; + s << INDENT << "if (mod && !strchr(str, '.'))\n"; { Indentation indent(INDENT); - s << INDENT << "return Shiboken::String::fromFormat(\"<%s.%s at %p>\", Shiboken::String::toCString(mod), str.constData(), self);" << endl; + s << INDENT << "return Shiboken::String::fromFormat(\"<%s.%s at %p>\", Shiboken::String::toCString(mod), str.constData(), self);\n"; } - s << INDENT << "else" << endl; + s << INDENT << "else\n"; { Indentation indent(INDENT); - s << INDENT << "return Shiboken::String::fromFormat(\"<%s at %p>\", str.constData(), self);" << endl; + s << INDENT << "return Shiboken::String::fromFormat(\"<%s at %p>\", str.constData(), self);\n"; } - s << '}' << endl; - s << "} // extern C" << endl << endl;; + s << "}\n"; + s << "} // extern C\n\n"; return funcName; } |