diff options
Diffstat (limited to 'sources/shiboken6/generator/shiboken/cppgenerator.cpp')
-rw-r--r-- | sources/shiboken6/generator/shiboken/cppgenerator.cpp | 6444 |
1 files changed, 6444 insertions, 0 deletions
diff --git a/sources/shiboken6/generator/shiboken/cppgenerator.cpp b/sources/shiboken6/generator/shiboken/cppgenerator.cpp new file mode 100644 index 000000000..1d7649b18 --- /dev/null +++ b/sources/shiboken6/generator/shiboken/cppgenerator.cpp @@ -0,0 +1,6444 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <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> + +#include <QtCore/QDir> +#include <QtCore/QMetaObject> +#include <QtCore/QRegularExpression> +#include <QtCore/QTextStream> +#include <QtCore/QDebug> +#include <QMetaType> + +#include <algorithm> +#include <cstring> + +static const char CPP_ARG0[] = "cppArg0"; + +QHash<QString, QString> CppGenerator::m_nbFuncs = QHash<QString, QString>(); +QHash<QString, QString> CppGenerator::m_sqFuncs = QHash<QString, QString>(); +QHash<QString, QString> CppGenerator::m_mpFuncs = QHash<QString, QString>(); +QString CppGenerator::m_currentErrorCode(QLatin1String("{}")); + +static const char typeNameFunc[] = R"CPP( +template <class T> +static const char *typeNameOf(const T &t) +{ + const char *typeName = typeid(t).name(); + auto size = std::strlen(typeName); +#if defined(Q_CC_MSVC) // MSVC: "class QPaintDevice * __ptr64" + if (auto lastStar = strchr(typeName, '*')) { + // MSVC: "class QPaintDevice * __ptr64" + while (*--lastStar == ' ') { + } + size = lastStar - typeName + 1; + } +#else // g++, Clang: "QPaintDevice *" -> "P12QPaintDevice" + if (size > 2 && typeName[0] == 'P' && std::isdigit(typeName[1])) { + ++typeName; + --size; + } +#endif + char *result = new char[size + 1]; + result[size] = '\0'; + memcpy(result, typeName, size); + return result; +} +)CPP"; + +// utility functions +inline AbstractMetaType getTypeWithoutContainer(const AbstractMetaType &arg) +{ + if (arg && arg.typeEntry()->isContainer()) { + // only support containers with 1 type + if (arg.instantiations().size() == 1) + return arg.instantiations().constFirst(); + } + return arg; +} + +// A helper for writing C++ return statements for either void ("return;") +// or some return value ("return value;") +class returnStatement +{ +public: + explicit returnStatement(QString s) : m_returnValue(std::move(s)) {} + + friend QTextStream &operator<<(QTextStream &s, const returnStatement &r); + +private: + const QString m_returnValue; +}; + +QTextStream &operator<<(QTextStream &s, const returnStatement &r) +{ + s << "return"; + if (!r.m_returnValue.isEmpty()) + s << ' ' << r.m_returnValue; + s << ';'; + return s; +} + +CppGenerator::CppGenerator() +{ + // Number protocol structure members names + m_nbFuncs.insert(QLatin1String("__add__"), QLatin1String("nb_add")); + m_nbFuncs.insert(QLatin1String("__sub__"), QLatin1String("nb_subtract")); + m_nbFuncs.insert(QLatin1String("__mul__"), QLatin1String("nb_multiply")); + m_nbFuncs.insert(QLatin1String("__div__"), QLatin1String("nb_divide")); + m_nbFuncs.insert(QLatin1String("__mod__"), QLatin1String("nb_remainder")); + m_nbFuncs.insert(QLatin1String("__neg__"), QLatin1String("nb_negative")); + m_nbFuncs.insert(QLatin1String("__pos__"), QLatin1String("nb_positive")); + m_nbFuncs.insert(QLatin1String("__invert__"), QLatin1String("nb_invert")); + m_nbFuncs.insert(QLatin1String("__lshift__"), QLatin1String("nb_lshift")); + m_nbFuncs.insert(QLatin1String("__rshift__"), QLatin1String("nb_rshift")); + m_nbFuncs.insert(QLatin1String("__and__"), QLatin1String("nb_and")); + m_nbFuncs.insert(QLatin1String("__xor__"), QLatin1String("nb_xor")); + m_nbFuncs.insert(QLatin1String("__or__"), QLatin1String("nb_or")); + m_nbFuncs.insert(QLatin1String("__iadd__"), QLatin1String("nb_inplace_add")); + m_nbFuncs.insert(QLatin1String("__isub__"), QLatin1String("nb_inplace_subtract")); + m_nbFuncs.insert(QLatin1String("__imul__"), QLatin1String("nb_inplace_multiply")); + m_nbFuncs.insert(QLatin1String("__idiv__"), QLatin1String("nb_inplace_divide")); + m_nbFuncs.insert(QLatin1String("__imod__"), QLatin1String("nb_inplace_remainder")); + m_nbFuncs.insert(QLatin1String("__ilshift__"), QLatin1String("nb_inplace_lshift")); + m_nbFuncs.insert(QLatin1String("__irshift__"), QLatin1String("nb_inplace_rshift")); + m_nbFuncs.insert(QLatin1String("__iand__"), QLatin1String("nb_inplace_and")); + m_nbFuncs.insert(QLatin1String("__ixor__"), QLatin1String("nb_inplace_xor")); + m_nbFuncs.insert(QLatin1String("__ior__"), QLatin1String("nb_inplace_or")); + m_nbFuncs.insert(QLatin1String("bool"), QLatin1String("nb_nonzero")); + + // sequence protocol functions + m_sequenceProtocol.insert(QLatin1String("__len__"), + {QLatin1String("PyObject *self"), + QLatin1String("Py_ssize_t")}); + m_sequenceProtocol.insert(QLatin1String("__getitem__"), + {QLatin1String("PyObject *self, Py_ssize_t _i"), + QLatin1String("PyObject*")}); + m_sequenceProtocol.insert(QLatin1String("__setitem__"), + {QLatin1String("PyObject *self, Py_ssize_t _i, PyObject *_value"), + 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"), + intT()}); + m_sequenceProtocol.insert(QLatin1String("__contains__"), + {QLatin1String("PyObject *self, PyObject *_value"), + intT()}); + m_sequenceProtocol.insert(QLatin1String("__concat__"), + {QLatin1String("PyObject *self, PyObject *_other"), + QLatin1String("PyObject*")}); + + // Sequence protocol structure members names + m_sqFuncs.insert(QLatin1String("__concat__"), QLatin1String("sq_concat")); + m_sqFuncs.insert(QLatin1String("__contains__"), QLatin1String("sq_contains")); + m_sqFuncs.insert(QLatin1String("__getitem__"), QLatin1String("sq_item")); + m_sqFuncs.insert(QLatin1String("__getslice__"), QLatin1String("sq_slice")); + m_sqFuncs.insert(QLatin1String("__len__"), QLatin1String("sq_length")); + m_sqFuncs.insert(QLatin1String("__setitem__"), QLatin1String("sq_ass_item")); + m_sqFuncs.insert(QLatin1String("__setslice__"), QLatin1String("sq_ass_slice")); + + // mapping protocol function + m_mappingProtocol.insert(QLatin1String("__mlen__"), + {QLatin1String("PyObject *self"), + QLatin1String("Py_ssize_t")}); + m_mappingProtocol.insert(QLatin1String("__mgetitem__"), + {QLatin1String("PyObject *self, PyObject *_key"), + QLatin1String("PyObject*")}); + m_mappingProtocol.insert(QLatin1String("__msetitem__"), + {QLatin1String("PyObject *self, PyObject *_key, PyObject *_value"), + intT()}); + + // Sequence protocol structure members names + m_mpFuncs.insert(QLatin1String("__mlen__"), QLatin1String("mp_length")); + m_mpFuncs.insert(QLatin1String("__mgetitem__"), QLatin1String("mp_subscript")); + m_mpFuncs.insert(QLatin1String("__msetitem__"), QLatin1String("mp_ass_subscript")); +} + +QString CppGenerator::fileNameSuffix() const +{ + return QLatin1String("_wrapper.cpp"); +} + +QString CppGenerator::fileNameForContext(const GeneratorContext &context) const +{ + const AbstractMetaClass *metaClass = context.metaClass(); + if (!context.forSmartPointer()) { + QString fileNameBase = metaClass->qualifiedCppName().toLower(); + fileNameBase.replace(QLatin1String("::"), QLatin1String("_")); + return fileNameBase + fileNameSuffix(); + } + const AbstractMetaType &smartPointerType = context.preciseType(); + QString fileNameBase = getFileNameBaseForSmartPointer(smartPointerType, metaClass); + return fileNameBase + fileNameSuffix(); +} + +QVector<AbstractMetaFunctionList> CppGenerator::filterGroupedOperatorFunctions(const AbstractMetaClass *metaClass, + uint queryIn) +{ + // ( func_name, num_args ) => func_list + QMap<QPair<QString, int>, AbstractMetaFunctionList> results; + const AbstractMetaClass::OperatorQueryOptions query(queryIn); + const AbstractMetaFunctionList &funcs = metaClass->operatorOverloads(query); + for (AbstractMetaFunction *func : funcs) { + if (func->isModifiedRemoved() + || func->usesRValueReferences() + || func->name() == QLatin1String("operator[]") + || func->name() == QLatin1String("operator->") + || func->name() == QLatin1String("operator!")) { + continue; + } + int args; + if (func->isComparisonOperator()) { + args = -1; + } else { + args = func->arguments().size(); + } + QPair<QString, int > op(func->name(), args); + results[op].append(func); + } + QVector<AbstractMetaFunctionList> result; + result.reserve(results.size()); + for (auto it = results.cbegin(), end = results.cend(); it != end; ++it) + result.append(it.value()); + return result; +} + +const AbstractMetaFunction *CppGenerator::boolCast(const AbstractMetaClass *metaClass) const +{ + if (!useIsNullAsNbNonZero()) + return nullptr; + // TODO: This could be configurable someday + const AbstractMetaFunction *func = metaClass->findFunction(QLatin1String("isNull")); + if (!func || func->isVoid() || !func->type().typeEntry()->isPrimitive() || !func->isPublic()) + return nullptr; + auto pte = static_cast<const PrimitiveTypeEntry *>(func->type().typeEntry()); + while (pte->referencedTypeEntry()) + pte = pte->referencedTypeEntry(); + return func && func->isConstant() && pte->name() == QLatin1String("bool") + && func->arguments().isEmpty() ? func : nullptr; +} + +AbstractMetaType CppGenerator::findSmartPointerInstantiation(const TypeEntry *entry) const +{ + for (const auto &i : instantiatedSmartPointers()) { + if (i.instantiations().at(0).typeEntry() == entry) + return i; + } + return {}; +} + +using FunctionGroupMap = QMap<QString, AbstractMetaFunctionList>; + +// Prevent ELF symbol qt_version_tag from being generated into the source +static const char includeQDebug[] = +"#ifndef QT_NO_VERSION_TAGGING\n" +"# define QT_NO_VERSION_TAGGING\n" +"#endif\n" +"#include <QDebug>\n"; + +static QString chopType(QString s) +{ + if (s.endsWith(QLatin1String("_Type"))) + s.chop(5); + else if (s.endsWith(QLatin1String("_TypeF()"))) + s.chop(8); + return s; +} + +// Helper for field setters: Check for "const QWidget *" (settable field), +// but not "int *const" (read-only field). +static bool isPointerToConst(const AbstractMetaType &t) +{ + const AbstractMetaType::Indirections &indirections = t.indirectionsV(); + return t.isConstant() && !indirections.isEmpty() + && indirections.constLast() != Indirection::ConstPointer; +} + +static inline bool canGenerateFieldSetter(const AbstractMetaField *field) +{ + const AbstractMetaType &type = field->type(); + return !type.isConstant() || isPointerToConst(type); +} + +static bool isStdSetterName(QString setterName, QString propertyName) +{ + return setterName.size() == propertyName.size() + 3 + && setterName.startsWith(QLatin1String("set")) + && setterName.endsWith(QStringView{propertyName}.right(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, const GeneratorContext &classContext) +{ + const AbstractMetaClass *metaClass = classContext.metaClass(); + + // write license comment + s << licenseComment() << Qt::endl; + + if (!avoidProtectedHack() && !metaClass->isNamespace() && !metaClass->hasPrivateDestructor()) { + s << "//workaround to access protected functions\n"; + s << "#define protected public\n\n"; + } + + // headers + s << "// default includes\n"; + s << "#include <shiboken.h>\n"; + if (usePySideExtensions()) { + s << includeQDebug; + s << "#include <pysidesignal.h>\n" + << "#include <pysideproperty.h>\n" + << "#include <pyside.h>\n" + << "#include <pysideqenum.h>\n" + << "#include <feature_select.h>\n" + << "#include <qapp_macro.h>\n\n" + << "QT_WARNING_DISABLE_DEPRECATED\n\n"; + } + + s << "#include <typeinfo>\n"; + if (usePySideExtensions() && metaClass->isQObject()) { + s << "#include <signalmanager.h>\n"; + s << "#include <pysidemetafunction.h>\n"; + } + + // The multiple inheritance initialization function + // needs the 'set' class from C++ STL. + if (getMultipleInheritingClass(metaClass) != nullptr) + s << "#include <algorithm>\n#include <set>\n"; + if (metaClass->generateExceptionHandling()) + s << "#include <exception>\n"; + s << "#include <iterator>\n"; // For containers + + if (wrapperDiagnostics()) + s << "#include <helper.h>\n#include <iostream>\n"; + + s << "\n// module include\n" << "#include \"" << getModuleHeaderFileName() << "\"\n"; + + QString headerfile = fileNameForContext(classContext); + headerfile.replace(QLatin1String(".cpp"), QLatin1String(".h")); + s << "\n// main header\n" << "#include \"" << headerfile << "\"\n"; + + s << Qt::endl << "// inner classes\n"; + const AbstractMetaClassList &innerClasses = metaClass->innerClasses(); + for (AbstractMetaClass *innerClass : innerClasses) { + GeneratorContext innerClassContext = contextForClass(innerClass); + if (shouldGenerate(innerClass) && !innerClass->typeEntry()->isSmartPointer()) { + QString headerfile = fileNameForContext(innerClassContext); + headerfile.replace(QLatin1String(".cpp"), QLatin1String(".h")); + s << "#include \"" << headerfile << "\"\n"; + } + } + + AbstractMetaEnumList classEnums = metaClass->enums(); + metaClass->getEnumsFromInvisibleNamespacesToBeGenerated(&classEnums); + + //Extra includes + QVector<Include> includes; + if (!classContext.useWrapper()) + includes += metaClass->typeEntry()->extraIncludes(); + for (AbstractMetaEnum *cppEnum : qAsConst(classEnums)) + includes.append(cppEnum->typeEntry()->extraIncludes()); + 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\n"; + + // Use class base namespace + { + const AbstractMetaClass *context = metaClass->enclosingClass(); + while (context) { + if (context->isNamespace() && !context->enclosingClass() + && static_cast<const NamespaceTypeEntry *>(context->typeEntry())->generateUsing()) { + s << "\nusing namespace " << context->qualifiedCppName() << ";\n"; + break; + } + context = context->enclosingClass(); + } + } + + s << Qt::endl << Qt::endl << typeNameFunc << Qt::endl; + + // Create string literal for smart pointer getter method. + if (classContext.forSmartPointer()) { + const auto *typeEntry = + static_cast<const SmartPointerTypeEntry *>(classContext.preciseType() + .typeEntry()); + QString rawGetter = typeEntry->getter(); + s << "static const char * " << SMART_POINTER_GETTER << " = \"" << rawGetter << "\";"; + } + + // class inject-code native/beginning + if (!metaClass->typeEntry()->codeSnips().isEmpty()) { + writeClassCodeSnips(s, metaClass->typeEntry()->codeSnips(), + TypeSystem::CodeSnipPositionBeginning, TypeSystem::NativeCode, + classContext); + s << Qt::endl; + } + + // python conversion rules + if (metaClass->typeEntry()->hasTargetConversionRule()) { + s << "// Python Conversion\n"; + s << metaClass->typeEntry()->conversionRule() << Qt::endl; + } + + if (classContext.useWrapper()) { + s << "// Native ---------------------------------------------------------\n\n"; + + if (avoidProtectedHack() && usePySideExtensions()) { + 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, classContext, func); + else if (shouldWriteVirtualMethodNative(func)) + writeVirtualMethodNative(s, func, maxOverrides++); + } + + if (!avoidProtectedHack() || !metaClass->hasPrivateDestructor()) { + if (usePySideExtensions() && metaClass->isQObject()) + writeMetaObjectMethod(s, classContext); + writeDestructorNative(s, classContext); + } + } + + Indentation indentation(INDENT); + + QString methodsDefinitions; + QTextStream md(&methodsDefinitions); + QString singleMethodDefinitions; + QTextStream smd(&singleMethodDefinitions); + QString signaturesString; + QTextStream signatureStream(&signaturesString); + + 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; + QSet<QString> seenSignatures; + bool staticEncountered = false; + for (AbstractMetaFunction *func : it.value()) { + if (!func->isAssignmentOperator() + && !func->usesRValueReferences() + && !func->isCastOperator() + && !func->isModifiedRemoved() + && (!func->isPrivate() || func->functionType() == AbstractMetaFunction::EmptyFunction) + && func->ownerClass() == func->implementingClass() + && (func->name() != QLatin1String("qt_metacall"))) { + // PYSIDE-331: Inheritance works correctly when there are disjoint functions. + // But when a function is both in a class and inherited in a subclass, + // then we need to search through all subclasses and collect the new signatures. + overloads << getFunctionAndInheritedOverloads(func, &seenSignatures); + if (func->isStatic()) + staticEncountered = true; + } + } + // PYSIDE-886: If the method does not have any static overloads declared + // in the class in question, remove all inherited static methods as setting + // METH_STATIC in that case can cause crashes for the instance methods. + // Manifested as crash when calling QPlainTextEdit::find() (clash with + // static QWidget::find(WId)). + if (!staticEncountered) { + for (int i = overloads.size() - 1; i >= 0; --i) { + if (overloads.at(i)->isStatic()) + delete overloads.takeAt(i); + } + } + + if (overloads.isEmpty()) + continue; + + const AbstractMetaFunction *rfunc = overloads.constFirst(); + if (m_sequenceProtocol.contains(rfunc->name()) || m_mappingProtocol.contains(rfunc->name())) + continue; + + if (rfunc->isConstructor()) { + // @TODO: Implement constructor support for smart pointers, so that they can be + // instantiated in python code. + if (classContext.forSmartPointer()) + continue; + writeConstructorWrapper(s, overloads, classContext); + writeSignatureInfo(signatureStream, overloads); + } + // call operators + else if (rfunc->name() == QLatin1String("operator()")) { + writeMethodWrapper(s, overloads, classContext); + writeSignatureInfo(signatureStream, overloads); + } + else if (!rfunc->isOperatorOverload()) { + + if (classContext.forSmartPointer()) { + const auto *smartPointerTypeEntry = + static_cast<const SmartPointerTypeEntry *>( + classContext.preciseType().typeEntry()); + + if (smartPointerTypeEntry->getter() == rfunc->name()) { + // Replace the return type of the raw pointer getter method with the actual + // return type. + QString innerTypeName = + classContext.preciseType().getSmartPointerInnerType().cppSignature(); + QString pointerToInnerTypeName = innerTypeName + QLatin1Char('*'); + // @TODO: This possibly leaks, but there are a bunch of other places where this + // is done, so this will be fixed in bulk with all the other cases, because the + // ownership of the pointers is not clear at the moment. + AbstractMetaType pointerToInnerType = + buildAbstractMetaTypeFromString(pointerToInnerTypeName); + + AbstractMetaFunction *mutableRfunc = overloads.constFirst(); + mutableRfunc->setType(pointerToInnerType); + } else if (smartPointerTypeEntry->refCountMethodName().isEmpty() + || smartPointerTypeEntry->refCountMethodName() != rfunc->name()) { + // Skip all public methods of the smart pointer except for the raw getter and + // the ref count method. + continue; + } + } + + writeMethodWrapper(s, overloads, classContext); + writeSignatureInfo(signatureStream, overloads); + if (OverloadData::hasStaticAndInstanceFunctions(overloads)) { + QString methDefName = cpythonMethodDefinitionName(rfunc); + smd << "static PyMethodDef " << methDefName << " = {\n"; + smd << INDENT; + writeMethodDefinitionEntry(smd, overloads); + smd << "\n};\n\n"; + } + writeMethodDefinition(md, overloads); + } + } + + const QString className = chopType(cpythonTypeName(metaClass)); + + if (metaClass->typeEntry()->isValue() || metaClass->typeEntry()->isSmartPointer()) { + writeCopyFunction(s, classContext); + 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[] = {\n"; + s << methodsDefinitions << Qt::endl; + if (metaClass->typeEntry()->isValue() || metaClass->typeEntry()->isSmartPointer()) { + s << INDENT << "{\"__copy__\", reinterpret_cast<PyCFunction>(" << className << "___copy__)" + << ", METH_NOARGS},\n"; + } + s << INDENT << '{' << NULL_PTR << ", " << NULL_PTR << "} // Sentinel\n"; + s << "};\n\n"; + + // Write tp_s/getattro function + const AttroCheck attroCheck = checkAttroFunctionNeeds(metaClass); + if (attroCheck.testFlag(AttroCheckFlag::GetattroSmartPointer)) { + writeSmartPointerGetattroFunction(s, classContext); + writeSmartPointerSetattroFunction(s, classContext); + } else { + 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)\n" + << "{\n"; + writeCppSelfDefinition(s, classContext); + if (f->allowThread()) { + 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();\n"; + } + s << "}\n\n"; + } + + if (supportsNumberProtocol(metaClass) && !metaClass->typeEntry()->isSmartPointer()) { + const QVector<AbstractMetaFunctionList> opOverloads = filterGroupedOperatorFunctions( + metaClass, + AbstractMetaClass::ArithmeticOp + | AbstractMetaClass::LogicalOp + | AbstractMetaClass::BitwiseOp); + + for (const AbstractMetaFunctionList &allOverloads : opOverloads) { + AbstractMetaFunctionList overloads; + for (AbstractMetaFunction *func : allOverloads) { + if (!func->isModifiedRemoved() + && !func->isPrivate() + && (func->ownerClass() == func->implementingClass() || func->isAbstract())) + overloads.append(func); + } + + if (overloads.isEmpty()) + continue; + + writeMethodWrapper(s, overloads, classContext); + writeSignatureInfo(signatureStream, overloads); + } + } + + if (supportsSequenceProtocol(metaClass)) { + writeSequenceMethods(s, metaClass, classContext); + } + + if (supportsMappingProtocol(metaClass)) { + writeMappingMethods(s, metaClass, classContext); + } + + if (!metaClass->isNamespace() && metaClass->hasComparisonOperatorOverload()) { + s << "// Rich comparison\n"; + writeRichCompareFunction(s, classContext); + } + + if (shouldGenerateGetSetList(metaClass) && !classContext.forSmartPointer()) { + const AbstractMetaFieldList &fields = metaClass->fields(); + for (const AbstractMetaField *metaField : fields) { + if (metaField->isStatic()) + continue; + writeGetterFunction(s, metaField, classContext); + if (canGenerateFieldSetter(metaField)) + writeSetterFunction(s, metaField, classContext); + s << Qt::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()) { + s << INDENT; + const QString setter = canGenerateFieldSetter(metaField) + ? cpythonSetterFunctionName(metaField) : QString(); + writePyGetSetDefEntry(s, metaField->name(), + cpythonGetterFunctionName(metaField), setter); + } + } + + 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\n"; + s << "};\n\n"; + } + + s << "} // extern \"C\"\n\n"; + + if (!metaClass->typeEntry()->hashFunction().isEmpty()) + writeHashFunction(s, classContext); + + // Write tp_traverse and tp_clear functions. + writeTpTraverseFunction(s, metaClass); + writeTpClearFunction(s, metaClass); + + writeClassDefinition(s, metaClass, classContext); + s << Qt::endl; + + if (metaClass->isPolymorphic() && metaClass->baseClass()) + writeTypeDiscoveryFunction(s, metaClass); + + 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()) { + writeClassCodeSnips(s, metaClass->typeEntry()->codeSnips(), + TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode, + classContext); + s << Qt::endl; + } +} + +void CppGenerator::writeCacheResetNative(QTextStream &s, const GeneratorContext &classContext) +{ + Indentation indentation(INDENT); + 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 << "\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\n"; + writeCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode, func, lastArg); + s << "}\n\n"; +} + +void CppGenerator::writeDestructorNative(QTextStream &s, const GeneratorContext &classContext) +{ + Indentation indentation(INDENT); + 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);\n"; + s << INDENT << "Shiboken::Object::destroy(wrapper, this);\n"; + s << "}\n"; +} + +static bool allArgumentsRemoved(const AbstractMetaFunction *func) +{ + if (func->arguments().isEmpty()) + return false; + const AbstractMetaArgumentList &arguments = func->arguments(); + for (const AbstractMetaArgument &arg : arguments) { + if (!func->argumentRemoved(arg.argumentIndex() + 1)) + return false; + } + return true; +} + +QString CppGenerator::getVirtualFunctionReturnTypeName(const AbstractMetaFunction *func) +{ + if (!func->type()) + return QLatin1String("\"\""); + + if (!func->typeReplaced(0).isEmpty()) + return QLatin1Char('"') + func->typeReplaced(0) + QLatin1Char('"'); + + // SbkType would return null when the type is a container. + auto typeEntry = func->type().typeEntry(); + if (typeEntry->isContainer()) { + return QLatin1Char('"') + + reinterpret_cast<const ContainerTypeEntry *>(typeEntry)->typeName() + + QLatin1Char('"'); + } + if (typeEntry->isSmartPointer()) + return QLatin1Char('"') + typeEntry->qualifiedCppName() + QLatin1Char('"'); + + if (avoidProtectedHack()) { + const AbstractMetaEnum *metaEnum = findAbstractMetaEnum(func->type()); + if (metaEnum && metaEnum->isProtected()) + return QLatin1Char('"') + protectedEnumSurrogateName(metaEnum) + QLatin1Char('"'); + } + + if (func->type().isPrimitive()) + return QLatin1Char('"') + func->type().name() + QLatin1Char('"'); + + 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"; +} + +// 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; + const int argId = match.capturedView(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); + } + 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().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) + << "\n{\n"; + + Indentation indentation(INDENT); + + 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 << "\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 (!snips.isEmpty()) { + writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionDeclaration, + TypeSystem::ShellCode, func, lastArg); + } + + 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); + writeVirtualMethodCppCall(s, func, funcName, snips, lastArg, retType, + returnStatement); + } + if (multi_line) + s << INDENT << "}\n"; + + s << INDENT << "Shiboken::GilState gil;\n"; + + // 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));\n"; + } else { + QStringList argConversions; + const AbstractMetaArgumentList &arguments = func->arguments(); + for (const AbstractMetaArgument &arg : arguments) { + if (func->argumentRemoved(arg.argumentIndex() + 1)) + continue; + + QString argConv; + QTextStream ac(&argConv); + const auto &argType = arg.type(); + auto argTypeEntry = static_cast<const PrimitiveTypeEntry *>(argType.typeEntry()); + bool convert = argTypeEntry->isObject() + || argTypeEntry->isValue() + || argType.isValuePointer() + || argType.isNativePointer() + || argTypeEntry->isFlags() + || argTypeEntry->isEnum() + || argTypeEntry->isContainer() + || argType.referenceType() == LValueReference; + + if (!convert && argTypeEntry->isPrimitive()) { + if (argTypeEntry->basicReferencedTypeEntry()) + argTypeEntry = argTypeEntry->basicReferencedTypeEntry(); + convert = !m_formatUnits.contains(argTypeEntry->name()); + } + + 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); + } else { + QString argName = arg.name(); + if (convert) + writeToPythonConversion(ac, arg.type(), func->ownerClass(), argName); + else + ac << argName; + } + + argConversions << argConv; + } + + s << "Py_BuildValue(\"(" << getFormatUnitString(func, false) << ")\",\n"; + s << argConversions.join(QLatin1String(",\n")) << Qt::endl; + s << INDENT << "));\n"; + } + + bool invalidateReturn = false; + QSet<int> invalidateArgs; + 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;\n"; + } else if (argMod.index == 0 && argMod.ownerships[TypeSystem::TargetLangCode] == TypeSystem::CppOwnership) { + invalidateReturn = true; + } + } + } + s << Qt::endl; + + if (!snips.isEmpty()) { + if (injectedCodeUsesPySelf(func)) + 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); + } + + if (!injectedCodeCallsPythonOverride(func)) { + s << INDENT; + s << "Shiboken::AutoDecRef " << PYTHON_RETURN_VAR << "(PyObject_Call(" + << PYTHON_OVERRIDE_VAR << ", " << PYTHON_ARGS << ", nullptr));\n"; + + s << INDENT << "// An error happened in python code!\n"; + s << INDENT << "if (" << PYTHON_RETURN_VAR << ".isNull()) {\n"; + { + Indentation indent(INDENT); + s << INDENT << "PyErr_Print();\n"; + s << INDENT << returnStatement << '\n'; + } + s << INDENT << "}\n"; + + if (!func->isVoid()) { + if (invalidateReturn) + s << INDENT << "bool invalidateArg0 = " << PYTHON_RETURN_VAR << "->ob_refcnt == 1;\n"; + + if (func->typeReplaced(0) != QLatin1String("PyObject")) { + + 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 << ");\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);\n"; + s << INDENT << returnStatement << '\n'; + } + s << INDENT << "}\n"; + + } else { + + 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 << ";\n"; + s << INDENT << "if (!typeIsValid"; + if (isPointerToWrapperType(func->type())) + s << " && " << PYTHON_RETURN_VAR << " != Py_None"; + 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);\n"; + s << INDENT << returnStatement << '\n'; + } + s << INDENT << "}\n"; + + } + } + + if (!func->conversionRule(TypeSystem::NativeCode, 0).isEmpty()) { + // Has conversion rule. + writeConversionRule(s, func, TypeSystem::NativeCode, QLatin1String(CPP_RETURN_VAR)); + } else if (!injectedCodeHasReturnValueAttribution(func, TypeSystem::NativeCode)) { + writePythonToCppTypeConversion(s, func->type(), QLatin1String(PYTHON_RETURN_VAR), + QLatin1String(CPP_RETURN_VAR), func->implementingClass()); + } + } + } + + if (invalidateReturn) { + 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 << ")\n" << indent(INDENT) + << INDENT << "Shiboken::Object::invalidate(PyTuple_GET_ITEM(" << PYTHON_ARGS + << ", " << (argIndex - 1) << "));\n" << outdent(INDENT); + } + + + 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 << "))\n"; + Indentation indent(INDENT); + s << INDENT << "Shiboken::Object::releaseOwnership(" << PYTHON_RETURN_VAR << ");\n"; + } + } + } + + if (func->hasInjectedCode()) { + s << Qt::endl; + const AbstractMetaArgument *lastArg = func->arguments().isEmpty() ? nullptr : &func->arguments().constLast(); + writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode, func, lastArg); + } + + if (!func->isVoid()) { + s << INDENT << "return "; + if (avoidProtectedHack() && retType->isEnum()) { + const AbstractMetaEnum *metaEnum = findAbstractMetaEnum(retType); + bool isProtectedEnum = metaEnum && metaEnum->isProtected(); + if (isProtectedEnum) { + QString typeCast; + if (metaEnum->enclosingClass()) + typeCast += QLatin1String("::") + metaEnum->enclosingClass()->qualifiedCppName(); + typeCast += QLatin1String("::") + metaEnum->name(); + s << '(' << typeCast << ')'; + } + } + if (func->type().referenceType() == LValueReference && !isPointer(func->type())) + s << " *"; + s << CPP_RETURN_VAR << ";\n"; + } + + s<< "}\n\n"; +} + +void CppGenerator::writeMetaObjectMethod(QTextStream &s, const GeneratorContext &classContext) +{ + Indentation indentation(INDENT); + 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)\n"; + s << "{\n"; + + AbstractMetaFunction *func = nullptr; + AbstractMetaFunctionList list = + classContext.metaClass()->queryFunctionsByName(QLatin1String("qt_metacall")); + if (list.size() == 1) + func = list[0]; + + CodeSnipList snips; + if (func) { + snips = func->injectedCodeSnips(); + if (func->isUserAdded()) { + CodeSnipList snips = func->injectedCodeSnips(); + writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionAny, TypeSystem::NativeCode, func); + } + } + + 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, classContext); +} + +void CppGenerator::writeMetaCast(QTextStream &s, const GeneratorContext &classContext) +{ + Indentation indentation(INDENT); + 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) +{ + if (metaEnum->isPrivate() || metaEnum->isAnonymous()) + return; + writeEnumConverterFunctions(s, metaEnum->typeEntry()); +} + +void CppGenerator::writeEnumConverterFunctions(QTextStream &s, const TypeEntry *enumType) +{ + if (!enumType) + return; + QString typeName = fixedCppTypeName(enumType); + QString enumPythonType = cpythonTypeNameExt(enumType); + QString cppTypeName = getFullTypeName(enumType).trimmed(); + if (avoidProtectedHack()) { + const AbstractMetaEnum *metaEnum = findAbstractMetaEnum(enumType); + if (metaEnum && metaEnum->isProtected()) + cppTypeName = protectedEnumSurrogateName(metaEnum); + } + QString code; + QTextStream c(&code); + 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 << ";\n"; + writePythonToCppFunction(s, code, typeName, typeName); + + QString pyTypeCheck = QStringLiteral("PyObject_TypeCheck(pyIn, %1)").arg(enumPythonType); + writeIsPythonConvertibleToCppFunction(s, typeName, typeName, pyTypeCheck); + + code.clear(); + + 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, " + << enumPythonType << "))"; + } else { + c << "Shiboken::Enum::newItem(" << enumPythonType << ", castCppIn)"; + } + c << ";\n"; + writeCppToPythonFunction(s, code, typeName, typeName); + s << Qt::endl; + + if (enumType->isFlags()) + return; + + auto flags = reinterpret_cast<const EnumTypeEntry *>(enumType)->flags(); + if (!flags) + return; + + // QFlags part. + + writeEnumConverterFunctions(s, flags); + + code.clear(); + cppTypeName = getFullTypeName(flags).trimmed(); + 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 << 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, + // then we would have been unable to detect the difference between + // a PolarOrientation and Qt::AlignmentFlag, which was the main + // issue of the bug. + const QString numberCondition = QStringLiteral("PyNumber_Check(pyIn) && ") + pyTypeCheck; + writePythonToCppFunction(s, code, QLatin1String("number"), flagsTypeName); + writeIsPythonConvertibleToCppFunction(s, QLatin1String("number"), flagsTypeName, numberCondition); + + +} + +void CppGenerator::writeConverterFunctions(QTextStream &s, const AbstractMetaClass *metaClass, + const GeneratorContext &classContext) +{ + s << "// Type conversion functions.\n\n"; + + AbstractMetaEnumList classEnums = metaClass->enums(); + metaClass->getEnumsFromInvisibleNamespacesToBeGenerated(&classEnums); + if (!classEnums.isEmpty()) + s << "// Python to C++ enum conversion.\n"; + for (const AbstractMetaEnum *metaEnum : qAsConst(classEnums)) + writeEnumConverterFunctions(s, metaEnum); + + if (metaClass->isNamespace()) + return; + + QString typeName; + if (!classContext.forSmartPointer()) + typeName = getFullTypeName(metaClass); + else + typeName = getFullTypeName(classContext.preciseType()); + + 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).\n"; + + QString sourceTypeName = metaClass->name(); + QString targetTypeName = metaClass->name() + QLatin1String("_PTR"); + QString code; + QTextStream c(&code); + 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 << 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).\n"; + code.clear(); + if (usePySideExtensions() && metaClass->isQObject()) + { + c << nested << "return PySide::getWrapperForQObject(reinterpret_cast<" + << typeName << " *>(const_cast<void *>(cppIn)), " << cpythonType << ");\n"; + } else { + c << nested << "auto pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(cppIn));\n"; + c << nested << "if (pyOut) {\n"; + { + 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" + << 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 << Qt::endl; + return; + } + + // Always copies C++ value (not pointer, and not reference) to a new Python wrapper. + s << Qt::endl << "// C++ to Python copy conversion.\n"; + if (!classContext.forSmartPointer()) + targetTypeName = metaClass->name(); + else + targetTypeName = classContext.preciseType().name(); + + sourceTypeName = targetTypeName + QLatin1String("_COPY"); + + code.clear(); + + QString computedWrapperName; + if (!classContext.forSmartPointer()) { + computedWrapperName = classContext.useWrapper() + ? classContext.wrapperName() : metaClass->qualifiedCppName(); + } else { + computedWrapperName = classContext.smartPointerWrapperName(); + } + + c << nested << "return Shiboken::Object::newObject(" << cpythonType + << ", new ::" << computedWrapperName << "(*reinterpret_cast<const " + << typeName << " *>(cppIn)), true, true);"; + writeCppToPythonFunction(s, code, sourceTypeName, targetTypeName); + s << Qt::endl; + + // Python to C++ copy conversion. + s << "// Python to C++ copy conversion.\n"; + if (!classContext.forSmartPointer()) + sourceTypeName = metaClass->name(); + else + sourceTypeName = classContext.preciseType().name(); + + targetTypeName = QStringLiteral("%1_COPY").arg(sourceTypeName); + code.clear(); + + QString pyInVariable = QLatin1String("pyIn"); + QString wrappedCPtrExpression; + if (!classContext.forSmartPointer()) + wrappedCPtrExpression = cpythonWrapperCPtr(metaClass->typeEntry(), pyInVariable); + else + wrappedCPtrExpression = cpythonWrapperCPtr(classContext.preciseType(), pyInVariable); + + 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 << Qt::endl; + + // User provided implicit conversions. + CustomConversion *customConversion = metaClass->typeEntry()->customConversion(); + + // Implicit conversions. + AbstractMetaFunctionList implicitConvs; + if (!customConversion || !customConversion->replaceOriginalTargetToNativeConversions()) { + const AbstractMetaFunctionList &allImplicitConvs = implicitConversions(metaClass->typeEntry()); + for (AbstractMetaFunction *func : allImplicitConvs) { + if (!func->isUserAdded()) + implicitConvs << func; + } + } + + if (!implicitConvs.isEmpty()) + s << "// Implicit conversions.\n"; + + AbstractMetaType targetType = buildAbstractMetaTypeFromAbstractMetaClass(metaClass); + for (const AbstractMetaFunction *conv : qAsConst(implicitConvs)) { + if (conv->isModifiedRemoved()) + continue; + + QString typeCheck; + QString toCppConv; + QString toCppPreConv; + if (conv->isConversionOperator()) { + const AbstractMetaClass *sourceClass = conv->ownerClass(); + typeCheck = QStringLiteral("PyObject_TypeCheck(pyIn, %1)").arg(cpythonTypeNameExt(sourceClass->typeEntry())); + toCppConv = QLatin1Char('*') + cpythonWrapperCPtr(sourceClass->typeEntry(), QLatin1String("pyIn")); + } else { + // Constructor that does implicit conversion. + if (!conv->typeReplaced(1).isEmpty() || conv->isModifiedToArray(1)) + continue; + const AbstractMetaType sourceType = conv->arguments().constFirst().type(); + typeCheck = cpythonCheckFunction(sourceType); + bool isUserPrimitiveWithoutTargetLangName = isUserPrimitive(sourceType) + && sourceType.typeEntry()->targetLangApiName() == sourceType.typeEntry()->name(); + if (!isWrapperType(sourceType) + && !isUserPrimitiveWithoutTargetLangName + && !sourceType.typeEntry()->isEnum() + && !sourceType.typeEntry()->isFlags() + && !sourceType.typeEntry()->isContainer()) { + typeCheck += QLatin1Char('('); + } + if (isWrapperType(sourceType)) { + typeCheck += QLatin1String("pyIn)"); + toCppConv = (sourceType.referenceType() == LValueReference || !isPointerToWrapperType(sourceType)) + ? QLatin1String(" *") : QString(); + toCppConv += cpythonWrapperCPtr(sourceType.typeEntry(), QLatin1String("pyIn")); + } else if (typeCheck.contains(QLatin1String("%in"))) { + typeCheck.replace(QLatin1String("%in"), QLatin1String("pyIn")); + typeCheck.append(QLatin1Char(')')); + } else { + typeCheck += QLatin1String("pyIn)"); + } + + if (isUserPrimitive(sourceType) + || isCppPrimitive(sourceType) + || sourceType.typeEntry()->isContainer() + || sourceType.typeEntry()->isEnum() + || sourceType.typeEntry()->isFlags()) { + QTextStream pc(&toCppPreConv); + pc << nested << getFullTypeNameWithoutModifiers(sourceType) << " cppIn"; + writeMinimalConstructorExpression(pc, sourceType); + pc << ";\n"; + writeToCppConversion(pc, sourceType, nullptr, QLatin1String("pyIn"), QLatin1String("cppIn")); + pc << ';'; + toCppConv.append(QLatin1String("cppIn")); + } else if (!isWrapperType(sourceType)) { + QTextStream tcc(&toCppConv); + writeToCppConversion(tcc, sourceType, metaClass, QLatin1String("pyIn"), QLatin1String("/*BOZO-1061*/")); + } + + + } + const AbstractMetaType sourceType = conv->isConversionOperator() + ? buildAbstractMetaTypeFromAbstractMetaClass(conv->ownerClass()) + : conv->arguments().constFirst().type(); + writePythonToCppConversionFunctions(s, sourceType, targetType, typeCheck, toCppConv, toCppPreConv); + } + + writeCustomConverterFunctions(s, customConversion); +} + +void CppGenerator::writeCustomConverterFunctions(QTextStream &s, const CustomConversion *customConversion) +{ + if (!customConversion) + return; + const CustomConversion::TargetToNativeConversions &toCppConversions = customConversion->targetToNativeConversions(); + if (toCppConversions.isEmpty()) + return; + s << "// Python to C++ conversions for type '" << customConversion->ownerType()->qualifiedCppName() << "'.\n"; + for (CustomConversion::TargetToNativeConversion *toNative : toCppConversions) + writePythonToCppConversionFunctions(s, toNative, customConversion->ownerType()); + s << Qt::endl; +} + +void CppGenerator::writeConverterRegister(QTextStream &s, const AbstractMetaClass *metaClass, + const GeneratorContext &classContext) +{ + if (metaClass->isNamespace()) + return; + s << INDENT << "// Register Converter\n"; + s << INDENT << "SbkConverter *converter = Shiboken::Conversions::createConverter("; + s << cpythonTypeName(metaClass) << ',' << Qt::endl; + { + Indentation indent(INDENT); + QString sourceTypeName = metaClass->name(); + QString targetTypeName = sourceTypeName + QLatin1String("_PTR"); + 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 << ',' << Qt::endl; + sourceTypeName = metaClass->name() + QLatin1String("_COPY"); + s << INDENT << cppToPythonFunctionName(sourceTypeName, targetTypeName); + } + } + 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(); + } + }; + + + if (!classContext.forSmartPointer()) { + writeConversionsForType(metaClass->qualifiedCppName()); + } else { + 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(::"; + QString qualifiedCppNameInvocation; + if (!classContext.forSmartPointer()) + qualifiedCppNameInvocation = metaClass->qualifiedCppName(); + else + qualifiedCppNameInvocation = classContext.preciseType().cppSignature(); + + s << qualifiedCppNameInvocation << ").name());\n"; + + if (classContext.useWrapper()) { + s << INDENT << "Shiboken::Conversions::registerConverterName(converter, typeid(::"; + s << classContext.wrapperName() << ").name());\n"; + } + + 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.\n"; + QString sourceTypeName = metaClass->name(); + QString targetTypeName = sourceTypeName + QLatin1String("_COPY"); + QString toCpp = pythonToCppFunctionName(sourceTypeName, targetTypeName); + QString isConv = convertibleToCppFunctionName(sourceTypeName, targetTypeName); + writeAddPythonToCppConversion(s, QLatin1String("converter"), toCpp, isConv); + + // User provided implicit conversions. + CustomConversion *customConversion = metaClass->typeEntry()->customConversion(); + + // Add implicit conversions. + AbstractMetaFunctionList implicitConvs; + if (!customConversion || !customConversion->replaceOriginalTargetToNativeConversions()) { + const AbstractMetaFunctionList &allImplicitConvs = implicitConversions(metaClass->typeEntry()); + for (AbstractMetaFunction *func : allImplicitConvs) { + if (!func->isUserAdded()) + implicitConvs << func; + } + } + + if (!implicitConvs.isEmpty()) + s << INDENT << "// Add implicit conversions to type converter.\n"; + + AbstractMetaType targetType = buildAbstractMetaTypeFromAbstractMetaClass(metaClass); + for (const AbstractMetaFunction *conv : qAsConst(implicitConvs)) { + if (conv->isModifiedRemoved()) + continue; + AbstractMetaType sourceType; + if (conv->isConversionOperator()) { + sourceType = buildAbstractMetaTypeFromAbstractMetaClass(conv->ownerClass()); + } else { + // Constructor that does implicit conversion. + if (!conv->typeReplaced(1).isEmpty() || conv->isModifiedToArray(1)) + continue; + sourceType = conv->arguments().constFirst().type(); + } + QString toCpp = pythonToCppFunctionName(sourceType, targetType); + QString isConv = convertibleToCppFunctionName(sourceType, targetType); + writeAddPythonToCppConversion(s, QLatin1String("converter"), toCpp, isConv); + } + + writeCustomConverterRegister(s, customConversion, QLatin1String("converter")); +} + +void CppGenerator::writeCustomConverterRegister(QTextStream &s, const CustomConversion *customConversion, const QString &converterVar) +{ + if (!customConversion) + return; + const CustomConversion::TargetToNativeConversions &toCppConversions = customConversion->targetToNativeConversions(); + if (toCppConversions.isEmpty()) + return; + 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()); + writeAddPythonToCppConversion(s, converterVar, toCpp, isConv); + } +} + +void CppGenerator::writeContainerConverterFunctions(QTextStream &s, const AbstractMetaType &containerType) +{ + writeCppToPythonFunction(s, containerType); + 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, + const GeneratorContext &context) +{ + const AbstractMetaFunction *rfunc = overloadData.referenceFunction(); + const AbstractMetaClass *ownerClass = rfunc->targetLangOwner(); + Q_ASSERT(ownerClass == context.metaClass()); + int minArgs = overloadData.minArgs(); + int maxArgs = overloadData.maxArgs(); + bool initPythonArguments; + bool usesNamedArguments; + + // If method is a constructor... + if (rfunc->isConstructor()) { + // Check if the right constructor was called. + if (!ownerClass->hasPrivateDestructor()) { + s << INDENT; + s << "if (Shiboken::Object::isUserType(self) && !Shiboken::ObjectType::canCallConstructor(self->ob_type, Shiboken::SbkType< ::"; + QString qualifiedCppName; + if (!context.forSmartPointer()) + qualifiedCppName = ownerClass->qualifiedCppName(); + else + qualifiedCppName = context.preciseType().cppSignature(); + + s << qualifiedCppName << " >()))\n"; + Indentation indent(INDENT); + s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl << Qt::endl; + } + // Declare pointer for the underlying C++ object. + s << INDENT << "::"; + if (!context.forSmartPointer()) { + s << (context.useWrapper() ? context.wrapperName() : ownerClass->qualifiedCppName()); + } else { + s << context.smartPointerWrapperName(); + } + s << " *cptr{};\n"; + + initPythonArguments = maxArgs > 0; + usesNamedArguments = !ownerClass->isQObject() && overloadData.hasArgumentWithDefaultValue(); + + } else { + if (rfunc->implementingClass() && + (!rfunc->implementingClass()->isNamespace() && overloadData.hasInstanceFunction())) { + writeCppSelfDefinition(s, rfunc, context, overloadData.hasStaticFunction()); + } + if (!rfunc->isInplaceOperator() && overloadData.hasNonVoidReturnType()) + s << INDENT << "PyObject *" << PYTHON_RETURN_VAR << "{};\n"; + + initPythonArguments = minArgs != maxArgs || maxArgs > 1; + usesNamedArguments = rfunc->isCallOperator() || overloadData.hasArgumentWithDefaultValue(); + } + + if (maxArgs > 0) { + 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 << " };\n"; + } else { + s << "{};\n"; + } + writeUnusedVariableCast(s, QLatin1String(PYTHON_TO_CPP_VAR)); + } + + if (usesNamedArguments && !rfunc->isCallOperator()) + s << INDENT << "const Py_ssize_t numNamedArgs = (kwds ? PyDict_Size(kwds) : 0);\n"; + + if (initPythonArguments) { + s << INDENT << "const Py_ssize_t numArgs = "; + if (minArgs == 0 && maxArgs == 1 && !rfunc->isConstructor() && !pythonFunctionWrapperUsesListOfArguments(overloadData)) + s << "(" << PYTHON_ARG << " == 0 ? 0 : 1);\n"; + else + writeArgumentsInitializer(s, overloadData); + } +} + +void CppGenerator::writeConstructorWrapper(QTextStream &s, const AbstractMetaFunctionList &overloads, + const GeneratorContext &classContext) +{ + ErrorCode errorCode(-1); + OverloadData overloadData(overloads, this); + + const AbstractMetaFunction *rfunc = overloadData.referenceFunction(); + const AbstractMetaClass *metaClass = rfunc->ownerClass(); + + 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{};\n"; + } else { + s << INDENT << "const char *argNames[] = {\"" + << argNamesList.join(QLatin1String("\", \"")) << "\"};\n"; + } + s << INDENT << "const QMetaObject *metaObject;\n"; + } + + 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);\n"; + s << INDENT << "SbkObjectType *myType = reinterpret_cast<SbkObjectType *>(" << cpythonTypeNameExt(metaClass->typeEntry()) << ");\n"; + } + + if (metaClass->isAbstract()) { + 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)\n" << indent(INDENT); + s << INDENT << "Shiboken::ObjectType::copyMultipleInheritance(type, myType);\n"; + if (!metaClass->isAbstract()) + s << outdent(INDENT) << '\n'; + } + + writeMethodWrapperPreamble(s, overloadData, classContext); + + s << Qt::endl; + + if (overloadData.maxArgs() > 0) + writeOverloadedFunctionDecisor(s, overloadData); + + writeFunctionCalls(s, overloadData, classContext); + s << Qt::endl; + + s << INDENT << "if (PyErr_Occurred() || !Shiboken::Object::setCppPointer(sbkSelf, Shiboken::SbkType< ::" << metaClass->qualifiedCppName() << " >(), cptr)) {\n"; + { + Indentation indent(INDENT); + s << INDENT << "delete cptr;\n"; + s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl; + } + s << INDENT << "}\n"; + if (overloadData.maxArgs() > 0) { + s << INDENT << "if (!cptr) goto " << cpythonFunctionName(rfunc) << "_TypeError;\n"; + s << Qt::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);\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)) {\n"; + { + Indentation indent(INDENT); + s << INDENT << "Shiboken::BindingManager::instance().releaseWrapper(Shiboken::BindingManager::instance().retrieveWrapper(cptr));\n"; + } + s << INDENT << "}\n"; + s << INDENT << "Shiboken::BindingManager::instance().registerWrapper(sbkSelf, cptr);\n"; + + // Create metaObject and register signal/slot + if (metaClass->isQObject() && usePySideExtensions()) { + 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 (kwds && !PySide::fillQtProperties(self, metaObject, kwds, argNames, " + << argNamesSet.count() << "))\n" << indent(INDENT) + << INDENT << returnStatement(m_currentErrorCode) << '\n' << outdent(INDENT); + } + + // Constructor code injections, position=end + bool hasCodeInjectionsAtEnd = false; + for (AbstractMetaFunction *func : overloads) { + const CodeSnipList &injectedCodeSnips = func->injectedCodeSnips(); + for (const CodeSnip &cs : injectedCodeSnips) { + if (cs.position == TypeSystem::CodeSnipPositionEnd) { + hasCodeInjectionsAtEnd = true; + break; + } + } + } + if (hasCodeInjectionsAtEnd) { + // FIXME: C++ arguments are not available in code injection on constructor when position = end. + 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) << ':' << Qt::endl; + s << INDENT << "{\n"; + { + Indentation indent(INDENT); + writeCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionEnd, TypeSystem::TargetLangCode, func); + } + s << INDENT << "}\n"; + break; + } + } + } + s << "}\n"; + } + + s << Qt::endl; + s << Qt::endl << INDENT << "return 1;\n"; + if (overloadData.maxArgs() > 0) + writeErrorSection(s, overloadData); + s<< "}\n\n"; +} + +void CppGenerator::writeMethodWrapper(QTextStream &s, const AbstractMetaFunctionList &overloads, + const GeneratorContext &classContext) +{ + OverloadData overloadData(overloads, this); + const AbstractMetaFunction *rfunc = overloadData.referenceFunction(); + + int maxArgs = overloadData.maxArgs(); + + s << "static PyObject *"; + s << cpythonFunctionName(rfunc) << "(PyObject *self"; + if (maxArgs > 0) { + s << ", PyObject *" << (pythonFunctionWrapperUsesListOfArguments(overloadData) ? "args" : PYTHON_ARG); + if (overloadData.hasArgumentWithDefaultValue() || rfunc->isCallOperator()) + s << ", PyObject *kwds"; + } + s << ")\n{\n"; + + writeMethodWrapperPreamble(s, overloadData, classContext); + + s << Qt::endl; + + /* + * This code is intended for shift operations only: + * Make sure reverse <</>> operators defined in other classes (specially from other modules) + * are called. A proper and generic solution would require an reengineering in the operator + * system like the extended converters. + * + * Solves #119 - QDataStream <</>> operators not working for QPixmap + * http://bugs.openbossa.org/show_bug.cgi?id=119 + */ + bool hasReturnValue = overloadData.hasNonVoidReturnType(); + bool callExtendedReverseOperator = hasReturnValue + && !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 << "\"));\n"; + s << INDENT << "if (!isReverse\n"; + { + Indentation indent(INDENT); + 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);\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);\n"; + s << INDENT << "if (PyErr_Occurred() && (PyErr_ExceptionMatches(PyExc_NotImplementedError)"; + s << " || PyErr_ExceptionMatches(PyExc_AttributeError))) {\n"; + { + Indentation indent(INDENT); + s << INDENT << "PyErr_Clear();\n"; + s << INDENT << "Py_XDECREF(" << PYTHON_RETURN_VAR << ");\n"; + s << INDENT << PYTHON_RETURN_VAR << " = " << NULL_PTR << ";\n"; + } + s << INDENT << "}\n"; + } + 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) + writeOverloadedFunctionDecisor(s, overloadData); + + writeFunctionCalls(s, overloadData, classContext); + + if (!reverseIndent.isNull()) { // binary shift operator + reverseIndent.reset(); + s << Qt::endl << INDENT << "} // End of \"if (!" << PYTHON_RETURN_VAR << ")\"\n"; + } + + s << Qt::endl; + + writeFunctionReturnErrorCheckSection(s, hasReturnValue && !rfunc->isInplaceOperator()); + + if (hasReturnValue) { + if (rfunc->isInplaceOperator()) { + s << INDENT << "Py_INCREF(self);\n"; + s << INDENT << "return self;\n"; + } else { + s << INDENT << "return " << PYTHON_RETURN_VAR << ";\n"; + } + } else { + s << INDENT << "Py_RETURN_NONE;\n"; + } + + if (maxArgs > 0) + writeErrorSection(s, overloadData); + + s<< "}\n\n"; +} + +void CppGenerator::writeArgumentsInitializer(QTextStream &s, OverloadData &overloadData) +{ + const AbstractMetaFunction *rfunc = overloadData.referenceFunction(); + s << "PyTuple_GET_SIZE(args);\n"; + writeUnusedVariableCast(s, QLatin1String("numArgs")); + + int minArgs = overloadData.minArgs(); + int maxArgs = overloadData.maxArgs(); + + s << INDENT << "PyObject *"; + s << PYTHON_ARGS << "[] = {" + << 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 << ");\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\n"; + bool ownerClassIsQObject = rfunc->ownerClass() && rfunc->ownerClass()->isQObject() && rfunc->isConstructor(); + if (usesNamedArguments) { + if (!ownerClassIsQObject) { + s << INDENT << "if (numArgs" << (overloadData.hasArgumentWithDefaultValue() ? " + numNamedArgs" : "") << " > " << maxArgs << ") {\n"; + { + Indentation indent(INDENT); + s << INDENT << "PyErr_SetString(PyExc_TypeError, \"" << fullPythonFunctionName(rfunc) << "(): too many arguments\");\n"; + s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl; + } + s << INDENT << '}'; + } + if (minArgs > 0) { + if (ownerClassIsQObject) + s << INDENT; + else + s << " else "; + s << "if (numArgs < " << minArgs << ") {\n"; + { + Indentation indent(INDENT); + s << INDENT << "PyErr_SetString(PyExc_TypeError, \"" << fullPythonFunctionName(rfunc) << "(): not enough arguments\");\n"; + s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl; + } + s << INDENT << '}'; + } + } + const QVector<int> invalidArgsLength = overloadData.invalidArgumentLengths(); + if (!invalidArgsLength.isEmpty()) { + QStringList invArgsLen; + for (int i : qAsConst(invalidArgsLength)) + invArgsLen << QStringLiteral("numArgs == %1").arg(i); + if (usesNamedArguments && (!ownerClassIsQObject || minArgs > 0)) + s << " else "; + else + s << INDENT; + s << "if (" << invArgsLen.join(QLatin1String(" || ")) << ")\n"; + Indentation indent(INDENT); + s << INDENT << "goto " << cpythonFunctionName(rfunc) << "_TypeError;"; + } + s << Qt::endl << Qt::endl; + + QString funcName; + if (rfunc->isOperatorOverload()) + funcName = ShibokenGenerator::pythonOperatorFunctionName(rfunc); + else + funcName = rfunc->name(); + + QString argsVar = overloadData.hasVarargs() ? QLatin1String("nonvarargs") : QLatin1String("args"); + s << INDENT << "if (!"; + if (usesNamedArguments) + s << "PyArg_ParseTuple(" << argsVar << ", \"|" << QByteArray(maxArgs, 'O') << ':' << funcName << '"'; + else + s << "PyArg_UnpackTuple(" << argsVar << ", \"" << funcName << "\", " << minArgs << ", " << maxArgs; + for (int i = 0; i < maxArgs; i++) + s << ", &(" << PYTHON_ARGS << '[' << i << "])"; + s << "))\n"; + { + Indentation indent(INDENT); + s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl; + } + s << Qt::endl; +} + +void CppGenerator::writeCppSelfConversion(QTextStream &s, const GeneratorContext &context, + const QString &className, bool useWrapperClass) +{ + static const QString pythonSelfVar = QLatin1String("self"); + if (useWrapperClass) + s << "static_cast<" << className << " *>("; + if (!context.forSmartPointer()) + s << cpythonWrapperCPtr(context.metaClass(), pythonSelfVar); + else + s << cpythonWrapperCPtr(context.preciseType(), pythonSelfVar); + if (useWrapperClass) + s << ')'; +} + +void CppGenerator::writeCppSelfDefinition(QTextStream &s, + const GeneratorContext &context, + bool hasStaticOverload, + bool cppSelfAsReference) +{ + Q_ASSERT(!(cppSelfAsReference && hasStaticOverload)); + + const AbstractMetaClass *metaClass = context.metaClass(); + bool useWrapperClass = avoidProtectedHack() && metaClass->hasProtectedMembers() + && !metaClass->attributes().testFlag(AbstractMetaAttributes::FinalCppClass); + Q_ASSERT(!useWrapperClass || context.useWrapper()); + QString className; + if (!context.forSmartPointer()) { + className = useWrapperClass + ? context.wrapperName() + : (QLatin1String("::") + metaClass->qualifiedCppName()); + } else { + className = context.smartPointerWrapperName(); + } + + writeInvalidPyObjectCheck(s, QLatin1String("self")); + + if (cppSelfAsReference) { + s << INDENT << "auto &" << CPP_SELF_VAR << " = *"; + writeCppSelfConversion(s, context, className, useWrapperClass); + s << ";\n"; + return; + } + + if (!hasStaticOverload) { + s << INDENT << "auto " << CPP_SELF_VAR << " = "; + writeCppSelfConversion(s, context, className, useWrapperClass); + s << ";\n"; + writeUnusedVariableCast(s, QLatin1String(CPP_SELF_VAR)); + return; + } + + 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, + const GeneratorContext &context, + bool hasStaticOverload) +{ + if (!func->ownerClass() || func->isConstructor()) + return; + + if (func->isOperatorOverload() && func->isBinaryOperator()) { + QString checkFunc = cpythonCheckFunction(func->ownerClass()->typeEntry()); + s << INDENT << "bool isReverse = " << checkFunc << PYTHON_ARG << ")\n"; + { + Indentation indent1(INDENT, 4); + s << INDENT << "&& !" << checkFunc << "self);\n"; + } + s << INDENT << "if (isReverse)\n"; + Indentation indent(INDENT); + s << INDENT << "std::swap(self, " << PYTHON_ARG << ");\n"; + } + + writeCppSelfDefinition(s, context, hasStaticOverload); +} + +void CppGenerator::writeErrorSection(QTextStream &s, OverloadData &overloadData) +{ + const AbstractMetaFunction *rfunc = overloadData.referenceFunction(); + s << Qt::endl << INDENT << cpythonFunctionName(rfunc) << "_TypeError:\n"; + Indentation indentation(INDENT); + QString funcName = fullPythonFunctionName(rfunc); + + QString argsVar = pythonFunctionWrapperUsesListOfArguments(overloadData) + ? QLatin1String("args") : QLatin1String(PYTHON_ARG); + s << INDENT << "Shiboken::setErrorAboutWrongArguments(" << argsVar << ", \"" << funcName << "\");\n"; + s << INDENT << "return " << m_currentErrorCode << ";\n"; +} + +void CppGenerator::writeFunctionReturnErrorCheckSection(QTextStream &s, bool hasReturnValue) +{ + s << INDENT << "if (PyErr_Occurred()"; + if (hasReturnValue) + s << " || !" << PYTHON_RETURN_VAR; + s << ") {\n"; + { + Indentation indent(INDENT); + if (hasReturnValue) + s << INDENT << "Py_XDECREF(" << PYTHON_RETURN_VAR << ");\n"; + s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl; + } + s << INDENT << "}\n"; +} + +void CppGenerator::writeInvalidPyObjectCheck(QTextStream &s, const QString &pyObj) +{ + s << INDENT << "if (!Shiboken::Object::isValid(" << pyObj << "))\n"; + Indentation indent(INDENT); + s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl; +} + +static QString pythonToCppConverterForArgumentName(const QString &argumentName) +{ + static const QRegularExpression pyArgsRegex(QLatin1String(PYTHON_ARGS) + + QLatin1String(R"((\[\d+[-]?\d*\]))")); + Q_ASSERT(pyArgsRegex.isValid()); + const QRegularExpressionMatch match = pyArgsRegex.match(argumentName); + QString result = QLatin1String(PYTHON_TO_CPP_VAR); + if (match.hasMatch()) + result += match.captured(1); + return result; +} + +void CppGenerator::writeTypeCheck(QTextStream &s, AbstractMetaType argType, + const QString &argumentName, bool isNumber, + const QString &customType, bool rejectNull) +{ + QString customCheck; + if (!customType.isEmpty()) { + AbstractMetaType metaType; + // PYSIDE-795: Note: XML-Overrides are handled in this shibokengenerator function! + // This enables iterables for QMatrix4x4 for instance. + customCheck = guessCPythonCheckFunction(customType, &metaType); + if (metaType) + argType = metaType; + } + + // TODO-CONVERTER: merge this with the code below. + QString typeCheck; + if (customCheck.isEmpty()) + typeCheck = cpythonIsConvertibleFunction(argType, argType.isEnum() ? false : isNumber); + else + typeCheck = customCheck; + typeCheck.append(QString::fromLatin1("(%1)").arg(argumentName)); + + // TODO-CONVERTER ----------------------------------------------------------------------- + if (customCheck.isEmpty() && !argType.typeEntry()->isCustom()) { + typeCheck = QString::fromLatin1("(%1 = %2))").arg(pythonToCppConverterForArgumentName(argumentName), typeCheck); + if (!isNumber && argType.typeEntry()->isCppPrimitive()) + typeCheck.prepend(QString::fromLatin1("%1(%2) && ").arg(cpythonCheckFunction(argType), argumentName)); + } + // TODO-CONVERTER ----------------------------------------------------------------------- + + if (rejectNull) + typeCheck = QString::fromLatin1("(%1 != Py_None && %2)").arg(argumentName, typeCheck); + + s << typeCheck; +} + +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) + || ShibokenGenerator::isCString(type) + || func->argumentRemoved(argIdx) + || !func->typeReplaced(argIdx).isEmpty() + || !func->conversionRule(TypeSystem::All, argIdx).isEmpty() + || func->hasInjectedCode()) + return; + QString message; + QTextStream str(&message); + 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() << '\''; + else + str << "type '" << type.cppSignature() << "' of argument " << argIdx; + str << " in function '"; + if (func->ownerClass()) + str << func->ownerClass()->qualifiedCppName() << "::"; + str << func->signature() << "'."; + qCWarning(lcShiboken).noquote().nospace() << message; +} + +static void checkTypeViability(const AbstractMetaFunction *func) +{ + if (func->isUserAdded()) + return; + checkTypeViability(func, func->type(), 0); + for (int i = 0; i < func->arguments().count(); ++i) + checkTypeViability(func, func->arguments().at(i).type(), i + 1); +} + +void CppGenerator::writeTypeCheck(QTextStream &s, const OverloadData *overloadData, QString argumentName) +{ + QSet<const TypeEntry *> numericTypes; + const OverloadDataList &overloads = overloadData->previousOverloadData()->nextOverloadData(); + for (OverloadData *od : overloads) { + const OverloadData::MetaFunctionList &odOverloads = od->overloads(); + for (const AbstractMetaFunction *func : odOverloads) { + checkTypeViability(func); + const AbstractMetaType &argType = od->argument(func)->type(); + if (!argType.isPrimitive()) + continue; + if (ShibokenGenerator::isNumber(argType.typeEntry())) + numericTypes << argType.typeEntry(); + } + } + + // This condition trusts that the OverloadData object will arrange for + // PyInt type to come after the more precise numeric types (e.g. float and bool) + AbstractMetaType argType = overloadData->argType(); + if (auto viewOn = argType.viewOn()) + argType = *viewOn; + bool numberType = numericTypes.count() == 1 || ShibokenGenerator::isPyInt(argType); + QString customType = (overloadData->hasArgumentTypeReplace() ? overloadData->argumentTypeReplaced() : QString()); + bool rejectNull = shouldRejectNullPointerArgument(overloadData->referenceFunction(), overloadData->argPos()); + writeTypeCheck(s, argType, argumentName, numberType, customType, rejectNull); +} + +void CppGenerator::writeArgumentConversion(QTextStream &s, + const AbstractMetaType &argType, + const QString &argName, const QString &pyArgName, + const AbstractMetaClass *context, + const QString &defaultValue, + bool castArgumentAsUnused) +{ + if (argType.typeEntry()->isCustom() || argType.typeEntry()->isVarargs()) + return; + if (isWrapperType(argType)) + writeInvalidPyObjectCheck(s, pyArgName); + writePythonToCppTypeConversion(s, argType, pyArgName, argName, context, defaultValue); + if (castArgumentAsUnused) + writeUnusedVariableCast(s, argName); +} + +const AbstractMetaType CppGenerator::getArgumentType(const AbstractMetaFunction *func, int argPos) +{ + if (argPos < 0 || argPos > func->arguments().size()) { + qCWarning(lcShiboken).noquote().nospace() + << QStringLiteral("Argument index for function '%1' out of range.").arg(func->signature()); + return {}; + } + + QString typeReplaced = func->typeReplaced(argPos); + if (typeReplaced.isEmpty()) { + if (argPos == 0) + return func->type(); + auto argType = func->arguments().at(argPos - 1).type(); + return argType.viewOn() ? *argType.viewOn() : argType; + } + + auto argType = buildAbstractMetaTypeFromString(typeReplaced); + if (!argType && !m_knownPythonTypes.contains(typeReplaced)) { + qCWarning(lcShiboken).noquote().nospace() + << QString::fromLatin1("Unknown type '%1' used as argument type replacement "\ + "in function '%2', the generated code may be broken.") + .arg(typeReplaced, func->signature()); + } + return argType; +} + +static inline QString arrayHandleType(const AbstractMetaTypeList &nestedArrayTypes) +{ + switch (nestedArrayTypes.size()) { + case 1: + return QStringLiteral("Shiboken::Conversions::ArrayHandle<") + + nestedArrayTypes.constLast().minimalSignature() + + QLatin1Char('>'); + case 2: + return QStringLiteral("Shiboken::Conversions::Array2Handle<") + + nestedArrayTypes.constLast().minimalSignature() + + QStringLiteral(", ") + + QString::number(nestedArrayTypes.constFirst().arrayElementCount()) + + QLatin1Char('>'); + } + return QString(); +} + +void CppGenerator::writePythonToCppTypeConversion(QTextStream &s, + const AbstractMetaType &type, + const QString &pyIn, + const QString &cppOut, + const AbstractMetaClass * /* context */, + const QString &defaultValue) +{ + const TypeEntry *typeEntry = type.typeEntry(); + if (typeEntry->isCustom() || typeEntry->isVarargs()) + return; + + QString cppOutAux = cppOut + QLatin1String("_local"); + + bool treatAsPointer = isValueTypeWithCopyConstructorOnly(type); + bool isPointerOrObjectType = (isObjectType(type) || isPointer(type)) && !isUserPrimitive(type) && !isCppPrimitive(type); + bool isNotContainerEnumOrFlags = !typeEntry->isContainer() && !typeEntry->isEnum() && !typeEntry->isFlags(); + bool mayHaveImplicitConversion = type.referenceType() == LValueReference + && !isUserPrimitive(type) + && !isCppPrimitive(type) + && isNotContainerEnumOrFlags + && !(treatAsPointer || isPointerOrObjectType); + + const AbstractMetaTypeList &nestedArrayTypes = type.nestedArrayTypes(); + const bool isCppPrimitiveArray = !nestedArrayTypes.isEmpty() + && nestedArrayTypes.constLast().isCppPrimitive(); + QString typeName = isCppPrimitiveArray + ? arrayHandleType(nestedArrayTypes) + : getFullTypeNameWithoutModifiers(type); + + bool isProtectedEnum = false; + + if (mayHaveImplicitConversion) { + s << INDENT << typeName << ' ' << cppOutAux; + writeMinimalConstructorExpression(s, type, defaultValue); + s << ";\n"; + } else if (avoidProtectedHack() && type.typeEntry()->isEnum()) { + const AbstractMetaEnum *metaEnum = findAbstractMetaEnum(type); + if (metaEnum && metaEnum->isProtected()) { + typeName = QLatin1String("long"); + isProtectedEnum = true; + } + } + + s << INDENT << typeName; + if (isCppPrimitiveArray) { + s << ' ' << cppOut; + } else if (treatAsPointer || isPointerOrObjectType) { + s << " *" << cppOut; + 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 { + s << ' ' << cppOut; + if (isProtectedEnum && avoidProtectedHack()) { + s << " = "; + if (defaultValue.isEmpty()) + s << "0"; + else + s << "(long)" << defaultValue; + } else if (isUserPrimitive(type) || typeEntry->isEnum() || typeEntry->isFlags()) { + writeMinimalConstructorExpression(s, typeEntry, defaultValue); + } else if (!type.isContainer() && !type.isSmartPointer()) { + writeMinimalConstructorExpression(s, type, defaultValue); + } + } + s << ";\n"; + + QString pythonToCppFunc = pythonToCppConverterForArgumentName(pyIn); + + s << INDENT; + if (!defaultValue.isEmpty()) + s << "if (" << pythonToCppFunc << ") "; + + QString pythonToCppCall = QString::fromLatin1("%1(%2, &%3)").arg(pythonToCppFunc, pyIn, cppOut); + if (!mayHaveImplicitConversion) { + s << pythonToCppCall << ";\n"; + return; + } + + if (!defaultValue.isEmpty()) + s << "{\n" << INDENT; + + s << "if (Shiboken::Conversions::isImplicitConversion(reinterpret_cast<SbkObjectType *>(" + << cpythonTypeNameExt(type) << "), " << pythonToCppFunc << "))\n"; + { + Indentation indent(INDENT); + s << INDENT << pythonToCppFunc << '(' << pyIn << ", &" << cppOutAux << ");\n"; + } + s << INDENT << "else\n"; + { + Indentation indent(INDENT); + s << INDENT << pythonToCppCall << ";\n"; + } + + if (!defaultValue.isEmpty()) + s << INDENT << '}'; + s << Qt::endl; +} + +static void addConversionRuleCodeSnippet(CodeSnipList &snippetList, QString &rule, + TypeSystem::Language /* conversionLanguage */, + TypeSystem::Language snippetLanguage, + const QString &outputName = QString(), + const QString &inputName = QString()) +{ + if (rule.isEmpty()) + return; + if (snippetLanguage == TypeSystem::TargetLangCode) { + rule.replace(QLatin1String("%in"), inputName); + rule.replace(QLatin1String("%out"), outputName + QLatin1String("_out")); + } else { + rule.replace(QLatin1String("%out"), outputName); + } + CodeSnip snip(snippetLanguage); + snip.position = (snippetLanguage == TypeSystem::NativeCode) ? TypeSystem::CodeSnipPositionAny : TypeSystem::CodeSnipPositionBeginning; + snip.addCode(rule); + snippetList << snip; +} + +void CppGenerator::writeConversionRule(QTextStream &s, const AbstractMetaFunction *func, TypeSystem::Language language) +{ + CodeSnipList snippets; + const AbstractMetaArgumentList &arguments = func->arguments(); + for (const AbstractMetaArgument &arg : arguments) { + QString rule = func->conversionRule(language, arg.argumentIndex() + 1); + addConversionRuleCodeSnippet(snippets, rule, language, TypeSystem::TargetLangCode, + arg.name(), arg.name()); + } + writeCodeSnips(s, snippets, TypeSystem::CodeSnipPositionBeginning, TypeSystem::TargetLangCode, func); +} + +void CppGenerator::writeConversionRule(QTextStream &s, const AbstractMetaFunction *func, TypeSystem::Language language, const QString &outputVar) +{ + CodeSnipList snippets; + QString rule = func->conversionRule(language, 0); + addConversionRuleCodeSnippet(snippets, rule, language, language, outputVar); + writeCodeSnips(s, snippets, TypeSystem::CodeSnipPositionAny, language, func); +} + +void CppGenerator::writeNoneReturn(QTextStream &s, const AbstractMetaFunction *func, bool thereIsReturnValue) +{ + 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\n"; + const AbstractMetaFunction *rfunc = overloadData.referenceFunction(); + const OverloadData::MetaFunctionList &functionOverloads = overloadData.overloadsWithoutRepetition(); + for (int i = 0; i < functionOverloads.count(); i++) { + const auto func = functionOverloads.at(i); + s << INDENT << "// " << i << ": "; + if (func->isStatic()) + s << "static "; + if (const auto *decl = func->declaringClass()) + s << decl->name() << "::"; + s << func->minimalSignature() << Qt::endl; + } + writeOverloadedFunctionDecisorEngine(s, &overloadData); + 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) {\n"; + { + Indentation indent(INDENT); + s << INDENT << "PyErr_SetString(PyExc_NotImplementedError, \"reverse operator not implemented.\");\n"; + s << INDENT << "return {};\n"; + } + s << INDENT << "}\n\n"; + } + + 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) +{ + bool hasDefaultCall = parentOverloadData->nextArgumentHasDefaultValue(); + const AbstractMetaFunction *referenceFunction = parentOverloadData->referenceFunction(); + + // If the next argument has not an argument with a default value, it is still possible + // that one of the overloads for the current overload data has its final occurrence here. + // If found, the final occurrence of a method is attributed to the referenceFunction + // variable to be used further on this method on the conditional that identifies default + // method calls. + if (!hasDefaultCall) { + const OverloadData::MetaFunctionList &overloads = parentOverloadData->overloads(); + for (const AbstractMetaFunction *func : overloads) { + if (parentOverloadData->isFinalOccurrence(func)) { + referenceFunction = func; + hasDefaultCall = true; + break; + } + } + } + + int maxArgs = parentOverloadData->maxArgs(); + // Python constructors always receive multiple arguments. + bool usePyArgs = pythonFunctionWrapperUsesListOfArguments(*parentOverloadData); + + // Functions without arguments are identified right away. + if (maxArgs == 0) { + s << INDENT << "overloadId = " << parentOverloadData->headOverloadData()->overloads().indexOf(referenceFunction); + s << "; // " << referenceFunction->minimalSignature() << Qt::endl; + return; + + } + // To decide if a method call is possible at this point the current overload + // data object cannot be the head, since it is just an entry point, or a root, + // for the tree of arguments and it does not represent a valid method call. + if (!parentOverloadData->isHeadOverloadData()) { + bool isLastArgument = parentOverloadData->nextOverloadData().isEmpty(); + bool signatureFound = parentOverloadData->overloads().size() == 1; + + // The current overload data describes the last argument of a signature, + // so the method can be identified right now. + if (isLastArgument || (signatureFound && !hasDefaultCall)) { + const AbstractMetaFunction *func = parentOverloadData->referenceFunction(); + s << INDENT << "overloadId = " << parentOverloadData->headOverloadData()->overloads().indexOf(func); + s << "; // " << func->minimalSignature() << Qt::endl; + return; + } + } + + bool isFirst = true; + + // If the next argument has a default value the decisor can perform a method call; + // it just need to check if the number of arguments received from Python are equal + // to the number of parameters preceding the argument with the default value. + const OverloadDataList &overloads = parentOverloadData->nextOverloadData(); + if (hasDefaultCall) { + isFirst = false; + int numArgs = parentOverloadData->argPos() + 1; + s << INDENT << "if (numArgs == " << numArgs << ") {\n"; + { + Indentation indent(INDENT); + const AbstractMetaFunction *func = referenceFunction; + for (OverloadData *overloadData : overloads) { + const AbstractMetaFunction *defValFunc = overloadData->getFunctionWithDefaultValue(); + if (defValFunc) { + func = defValFunc; + break; + } + } + s << INDENT << "overloadId = " << parentOverloadData->headOverloadData()->overloads().indexOf(func); + s << "; // " << func->minimalSignature() << Qt::endl; + } + s << INDENT << '}'; + } + + for (OverloadData *overloadData : overloads) { + bool signatureFound = overloadData->overloads().size() == 1 + && !overloadData->getFunctionWithDefaultValue() + && !overloadData->findNextArgWithDefault(); + + const AbstractMetaFunction *refFunc = overloadData->referenceFunction(); + + QStringList typeChecks; + + QString pyArgName = (usePyArgs && maxArgs > 1) + ? pythonArgsAt(overloadData->argPos()) + : QLatin1String(PYTHON_ARG); + OverloadData *od = overloadData; + int startArg = od->argPos(); + int sequenceArgCount = 0; + while (od && !od->argType().isVarargs()) { + bool typeReplacedByPyObject = od->argumentTypeReplaced() == QLatin1String("PyObject"); + if (!typeReplacedByPyObject) { + if (usePyArgs) + pyArgName = pythonArgsAt(od->argPos()); + QString typeCheck; + QTextStream tck(&typeCheck); + const AbstractMetaFunction *func = od->referenceFunction(); + + if (func->isConstructor() && func->arguments().count() == 1) { + 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 << ")\n"; + Indentation indent(INDENT); + tck << INDENT << "&& "; + } + } + writeTypeCheck(tck, od, pyArgName); + typeChecks << typeCheck; + } + + sequenceArgCount++; + + if (od->nextOverloadData().isEmpty() + || od->nextArgumentHasDefaultValue() + || od->nextOverloadData().size() != 1 + || od->overloads().size() != od->nextOverloadData().constFirst()->overloads().size()) { + overloadData = od; + od = nullptr; + } else { + od = od->nextOverloadData().constFirst(); + } + } + + if (usePyArgs && signatureFound) { + AbstractMetaArgumentList args = refFunc->arguments(); + 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()) { + typeChecks.prepend(QString::fromLatin1("%1isReverse").arg(refFunc->isReverseOperator() ? QString() : QLatin1String("!"))); + } + + if (isFirst) { + isFirst = false; + s << INDENT; + } else { + s << " else "; + } + s << "if ("; + if (typeChecks.isEmpty()) { + s << "true"; + } else { + Indentation indent(INDENT); + QString separator; + QTextStream sep(&separator); + sep << Qt::endl << INDENT << "&& "; + s << typeChecks.join(separator); + } + s << ") {\n"; + { + Indentation indent(INDENT); + writeOverloadedFunctionDecisorEngine(s, overloadData); + } + s << INDENT << "}"; + } + s << Qt::endl; +} + +void CppGenerator::writeFunctionCalls(QTextStream &s, const OverloadData &overloadData, + const GeneratorContext &context) +{ + const OverloadData::MetaFunctionList &overloads = overloadData.overloadsWithoutRepetition(); + s << INDENT << "// Call function/method\n"; + s << INDENT << (overloads.count() > 1 ? "switch (overloadId) " : "") << "{\n"; + { + Indentation indent(INDENT); + if (overloads.count() == 1) { + writeSingleFunctionCall(s, overloadData, overloads.constFirst(), context); + } else { + for (int i = 0; i < overloads.count(); i++) { + const AbstractMetaFunction *func = overloads.at(i); + s << INDENT << "case " << i << ": // " << func->signature() << Qt::endl; + s << INDENT << "{\n"; + { + Indentation indent(INDENT); + writeSingleFunctionCall(s, overloadData, func, context); + if (func->attributes().testFlag(AbstractMetaAttributes::Deprecated)) { + s << INDENT << "PyErr_WarnEx(PyExc_DeprecationWarning, \""; + if (auto cls = context.metaClass()) + s << cls->name() << '.'; + s << func->signature() << " is deprecated\", 1);\n"; + } + s << INDENT << "break;\n"; + } + s << INDENT << "}\n"; + } + } + } + s << INDENT << "}\n"; +} + +void CppGenerator::writeSingleFunctionCall(QTextStream &s, + const OverloadData &overloadData, + const AbstractMetaFunction *func, + 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.\");\n"; + } + + if (func->functionType() == AbstractMetaFunction::EmptyFunction) { + s << INDENT << "PyErr_Format(PyExc_TypeError, \"%s is a private method.\", \"" + << func->signature().replace(QLatin1String("::"), QLatin1String(".")) + << "\");\n"; + s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl; + return; + } + + bool usePyArgs = pythonFunctionWrapperUsesListOfArguments(overloadData); + + // Handle named arguments. + writeNamedArgumentResolution(s, func, usePyArgs); + + bool injectCodeCallsFunc = injectedCodeCallsCppFunction(context, func); + bool mayHaveUnunsedArguments = !func->isUserAdded() && func->hasInjectedCode() && injectCodeCallsFunc; + int removedArgs = 0; + for (int argIdx = 0; argIdx < func->arguments().count(); ++argIdx) { + bool hasConversionRule = !func->conversionRule(TypeSystem::NativeCode, argIdx + 1).isEmpty(); + const AbstractMetaArgument &arg = func->arguments().at(argIdx); + if (func->argumentRemoved(argIdx + 1)) { + if (!arg.defaultValueExpression().isEmpty()) { + const QString cppArgRemoved = QLatin1String(CPP_ARG_REMOVED) + + QString::number(argIdx); + s << INDENT << getFullTypeName(arg.type()) << ' ' << cppArgRemoved; + 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 + // the method are provided (as with code injection) the generator must abort. + qFatal("No way to call '%s::%s' with the modifications described in the type system.", + qPrintable(func->ownerClass()->name()), qPrintable(func->signature())); + } + removedArgs++; + continue; + } + if (hasConversionRule) + continue; + const AbstractMetaType argType = getArgumentType(func, argIdx + 1); + if (!argType || (mayHaveUnunsedArguments && !injectedCodeUsesArgument(func, argIdx))) + continue; + int argPos = argIdx - removedArgs; + QString argName = QLatin1String(CPP_ARG) + QString::number(argPos); + QString pyArgName = usePyArgs ? pythonArgsAt(argPos) : QLatin1String(PYTHON_ARG); + QString defaultValue = guessScopeForDefaultValue(func, arg); + writeArgumentConversion(s, argType, argName, pyArgName, func->implementingClass(), defaultValue, func->isUserAdded()); + } + + s << Qt::endl; + + int numRemovedArgs = OverloadData::numberOfRemovedArguments(func); + + 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) +{ + if (targetTypeName.isEmpty()) + targetTypeName = sourceTypeName; + return QString::fromLatin1("%1_CppToPython_%2").arg(sourceTypeName, targetTypeName); +} + +QString CppGenerator::pythonToCppFunctionName(const QString &sourceTypeName, const QString &targetTypeName) +{ + return QString::fromLatin1("%1_PythonToCpp_%2").arg(sourceTypeName, targetTypeName); +} +QString CppGenerator::pythonToCppFunctionName(const AbstractMetaType &sourceType, const AbstractMetaType &targetType) +{ + return pythonToCppFunctionName(fixedCppTypeName(sourceType), fixedCppTypeName(targetType)); +} +QString CppGenerator::pythonToCppFunctionName(const CustomConversion::TargetToNativeConversion *toNative, + const TypeEntry *targetType) +{ + return pythonToCppFunctionName(fixedCppTypeName(toNative), fixedCppTypeName(targetType)); +} + +QString CppGenerator::convertibleToCppFunctionName(const QString &sourceTypeName, const QString &targetTypeName) +{ + return QString::fromLatin1("is_%1_PythonToCpp_%2_Convertible").arg(sourceTypeName, targetTypeName); +} +QString CppGenerator::convertibleToCppFunctionName(const AbstractMetaType &sourceType, const AbstractMetaType &targetType) +{ + return convertibleToCppFunctionName(fixedCppTypeName(sourceType), fixedCppTypeName(targetType)); +} +QString CppGenerator::convertibleToCppFunctionName(const CustomConversion::TargetToNativeConversion *toNative, + const TypeEntry *targetType) +{ + return convertibleToCppFunctionName(fixedCppTypeName(toNative), fixedCppTypeName(targetType)); +} + +void CppGenerator::writeCppToPythonFunction(QTextStream &s, const QString &code, const QString &sourceTypeName, QString targetTypeName) +{ + QString prettyCode; + QTextStream c(&prettyCode); + formatCode(c, code, INDENT); + processCodeSnip(prettyCode); + + s << "static PyObject *" << cppToPythonFunctionName(sourceTypeName, targetTypeName); + s << "(const void *cppIn) {\n"; + s << prettyCode; + s << "}\n"; +} + +static void replaceCppToPythonVariables(QString &code, const QString &typeName) +{ + 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")); + code.replace(QLatin1String("%out"), QLatin1String("pyOut")); +} +void CppGenerator::writeCppToPythonFunction(QTextStream &s, const CustomConversion *customConversion) +{ + QString code = customConversion->nativeToTargetConversion(); + replaceCppToPythonVariables(code, getFullTypeName(customConversion->ownerType())); + writeCppToPythonFunction(s, code, fixedCppTypeName(customConversion->ownerType())); +} +void CppGenerator::writeCppToPythonFunction(QTextStream &s, const AbstractMetaType &containerType) +{ + const CustomConversion *customConversion = containerType.typeEntry()->customConversion(); + if (!customConversion) { + qFatal("Can't write the C++ to Python conversion function for container type '%s' - "\ + "no conversion rule was defined for it in the type system.", + qPrintable(containerType.typeEntry()->qualifiedCppName())); + } + if (!containerType.typeEntry()->isContainer()) { + writeCppToPythonFunction(s, customConversion); + return; + } + QString code = customConversion->nativeToTargetConversion(); + for (int i = 0; i < containerType.instantiations().count(); ++i) { + const AbstractMetaType &type = containerType.instantiations().at(i); + QString typeName = getFullTypeName(type); + if (type.isConstant()) + typeName = QLatin1String("const ") + typeName; + code.replace(QString::fromLatin1("%INTYPE_%1").arg(i), typeName); + } + replaceCppToPythonVariables(code, getFullTypeNameWithoutModifiers(containerType)); + processCodeSnip(code); + writeCppToPythonFunction(s, code, fixedCppTypeName(containerType)); +} + +void CppGenerator::writePythonToCppFunction(QTextStream &s, const QString &code, const QString &sourceTypeName, const QString &targetTypeName) +{ + QString prettyCode; + QTextStream c(&prettyCode); + formatCode(c, code, INDENT); + processCodeSnip(prettyCode); + s << "static void " << pythonToCppFunctionName(sourceTypeName, targetTypeName); + s << "(PyObject *pyIn, void *cppOut) {\n"; + s << prettyCode; + s << "}\n"; +} + +void CppGenerator::writeIsPythonConvertibleToCppFunction(QTextStream &s, + const QString &sourceTypeName, + const QString &targetTypeName, + const QString &condition, + QString pythonToCppFuncName, + bool acceptNoneAsCppNull) +{ + if (pythonToCppFuncName.isEmpty()) + pythonToCppFuncName = pythonToCppFunctionName(sourceTypeName, targetTypeName); + + s << "static PythonToCppFunc " << convertibleToCppFunctionName(sourceTypeName, targetTypeName); + s << "(PyObject *pyIn) {\n"; + if (acceptNoneAsCppNull) { + s << INDENT << "if (pyIn == Py_None)\n"; + Indentation indent(INDENT); + s << INDENT << "return Shiboken::Conversions::nonePythonToCppNullPtr;\n"; + } + s << INDENT << "if (" << condition << ")\n"; + { + Indentation indent(INDENT); + s << INDENT << "return " << pythonToCppFuncName << ";\n"; + } + s << INDENT << "return {};\n"; + s << "}\n"; +} + +void CppGenerator::writePythonToCppConversionFunctions(QTextStream &s, + const AbstractMetaType &sourceType, + const AbstractMetaType &targetType, + QString typeCheck, + QString conversion, + const QString &preConversion) +{ + QString sourcePyType = cpythonTypeNameExt(sourceType); + + // Python to C++ conversion function. + QString code; + QTextStream c(&code); + Indentor nested; + if (conversion.isEmpty()) + conversion = QLatin1Char('*') + cpythonWrapperCPtr(sourceType, QLatin1String("pyIn")); + if (!preConversion.isEmpty()) + 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); + writePythonToCppFunction(s, code, sourceTypeName, targetTypeName); + + // Python to C++ convertible check function. + if (typeCheck.isEmpty()) + typeCheck = QString::fromLatin1("PyObject_TypeCheck(pyIn, %1)").arg(sourcePyType); + writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, typeCheck); + s << Qt::endl; +} + +void CppGenerator::writePythonToCppConversionFunctions(QTextStream &s, + const CustomConversion::TargetToNativeConversion *toNative, + const TypeEntry *targetType) +{ + // Python to C++ conversion function. + QString code = toNative->conversion(); + QString inType; + if (toNative->sourceType()) + inType = cpythonTypeNameExt(toNative->sourceType()); + else + inType = QString::fromLatin1("(%1_TypeF())").arg(toNative->sourceTypeName()); + code.replace(QLatin1String("%INTYPE"), inType); + code.replace(QLatin1String("%OUTTYPE"), targetType->qualifiedCppName()); + code.replace(QLatin1String("%in"), QLatin1String("pyIn")); + code.replace(QLatin1String("%out"), + QLatin1String("*reinterpret_cast<") + getFullTypeName(targetType) + QLatin1String(" *>(cppOut)")); + + QString sourceTypeName = fixedCppTypeName(toNative); + QString targetTypeName = fixedCppTypeName(targetType); + writePythonToCppFunction(s, code, sourceTypeName, targetTypeName); + + // Python to C++ convertible check function. + QString typeCheck = toNative->sourceTypeCheck(); + if (typeCheck.isEmpty()) { + QString pyTypeName = toNative->sourceTypeName(); + if (pyTypeName == QLatin1String("Py_None") || pyTypeName == QLatin1String("PyNone")) + typeCheck = QLatin1String("%in == Py_None"); + else if (pyTypeName == QLatin1String("SbkEnumType")) + typeCheck = QLatin1String("Shiboken::isShibokenEnum(%in)"); + else if (pyTypeName == QLatin1String("SbkObject")) + typeCheck = QLatin1String("Shiboken::Object::checkType(%in)"); + else if (pyTypeName == QLatin1String("PyTypeObject")) + typeCheck = QLatin1String("PyType_Check(%in)"); + else if (pyTypeName == QLatin1String("PyObject")) + typeCheck = QLatin1String("PyObject_TypeCheck(%in, &PyBaseObject_Type)"); + // PYSIDE-795: We abuse PySequence for iterables + else if (pyTypeName == QLatin1String("PySequence")) + typeCheck = QLatin1String("Shiboken::String::checkIterable(%in)"); + else if (pyTypeName.startsWith(QLatin1String("Py"))) + typeCheck = pyTypeName + QLatin1String("_Check(%in)"); + } + if (typeCheck.isEmpty()) { + if (!toNative->sourceType() || toNative->sourceType()->isPrimitive()) { + qFatal("User added implicit conversion for C++ type '%s' must provide either an input "\ + "type check function or a non primitive type entry.", + qPrintable(targetType->qualifiedCppName())); + + } + typeCheck = QString::fromLatin1("PyObject_TypeCheck(%in, %1)").arg(cpythonTypeNameExt(toNative->sourceType())); + } + typeCheck.replace(QLatin1String("%in"), QLatin1String("pyIn")); + processCodeSnip(typeCheck); + writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, typeCheck); +} + +void CppGenerator::writePythonToCppConversionFunctions(QTextStream &s, const AbstractMetaType &containerType) +{ + const CustomConversion *customConversion = containerType.typeEntry()->customConversion(); + if (!customConversion) { + //qFatal + return; + } + const CustomConversion::TargetToNativeConversions &toCppConversions = customConversion->targetToNativeConversions(); + if (toCppConversions.isEmpty()) { + //qFatal + return; + } + // Python to C++ conversion function. + QString cppTypeName = getFullTypeNameWithoutModifiers(containerType); + 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); + if (type.isValue() && isValueTypeWithCopyConstructorOnly(type)) { + for (int pos = 0; ; ) { + const QRegularExpressionMatch match = convertToCppRegEx().match(code, pos); + if (!match.hasMatch()) + break; + pos = match.capturedEnd(); + const QString varName = match.captured(1); + QString rightCode = code.mid(pos); + rightCode.replace(varName, QLatin1Char('*') + varName); + code.replace(pos, code.size() - pos, rightCode); + } + typeName.append(QLatin1String(" *")); + } + code.replace(QString::fromLatin1("%OUTTYPE_%1").arg(i), typeName); + } + code.replace(QLatin1String("%OUTTYPE"), cppTypeName); + code.replace(QLatin1String("%in"), QLatin1String("pyIn")); + code.replace(QLatin1String("%out"), QLatin1String("cppOutRef")); + QString typeName = fixedCppTypeName(containerType); + writePythonToCppFunction(s, code, typeName, typeName); + + // Python to C++ convertible check function. + QString typeCheck = cpythonCheckFunction(containerType); + if (typeCheck.isEmpty()) + typeCheck = QLatin1String("false"); + else + typeCheck = QString::fromLatin1("%1pyIn)").arg(typeCheck); + writeIsPythonConvertibleToCppFunction(s, typeName, typeName, typeCheck); + s << Qt::endl; +} + +void CppGenerator::writeAddPythonToCppConversion(QTextStream &s, const QString &converterVar, const QString &pythonToCppFunc, const QString &isConvertibleFunc) +{ + s << INDENT << "Shiboken::Conversions::addPythonToCppValueConversion(" << converterVar << ',' << Qt::endl; + { + Indentation indent(INDENT); + s << INDENT << pythonToCppFunc << ',' << Qt::endl; + s << INDENT << isConvertibleFunc; + } + s << ");\n"; +} + +void CppGenerator::writeNamedArgumentResolution(QTextStream &s, const AbstractMetaFunction *func, bool usePyArgs) +{ + const AbstractMetaArgumentList &args = OverloadData::getArgumentsWithDefaultValues(func); + if (args.isEmpty()) + return; + + QString pyErrString(QLatin1String("PyErr_SetString(PyExc_TypeError, \"") + fullPythonFunctionName(func) + + QLatin1String("(): got multiple values for keyword argument '%1'.\");")); + + s << INDENT << "if (kwds) {\n"; + { + Indentation indent(INDENT); + s << INDENT << "PyObject *keyName = nullptr;\n"; + s << INDENT << "PyObject *value = nullptr;\n"; + for (const AbstractMetaArgument &arg : args) { + 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() << "\");\n"; + s << INDENT << "if (PyDict_Contains(kwds, keyName)) {\n"; + { + Indentation indent(INDENT); + s << INDENT << "value = PyDict_GetItem(kwds, keyName);\n"; + s << INDENT << "if (value && " << pyArgName << ") {\n"; + { + Indentation indent(INDENT); + s << INDENT << pyErrString.arg(arg.name()) << Qt::endl; + s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl; + } + s << INDENT << "}\n"; + s << INDENT << "if (value) {\n"; + { + Indentation indent(INDENT); + s << INDENT << pyArgName << " = value;\n"; + s << INDENT << "if (!"; + 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;\n"; + } + } + s << INDENT << "}\n"; + } + s << INDENT << "}\n"; + } + } + s << INDENT << "}\n"; +} + +QString CppGenerator::argumentNameFromIndex(const AbstractMetaFunction *func, int argIndex, const AbstractMetaClass **wrappedClass) +{ + *wrappedClass = nullptr; + QString pyArgName; + if (argIndex == -1) { + pyArgName = QLatin1String("self"); + *wrappedClass = func->implementingClass(); + } else if (argIndex == 0) { + const auto funcType = func->type(); + AbstractMetaType returnType = getTypeWithoutContainer(funcType); + if (!returnType.isVoid()) { + pyArgName = QLatin1String(PYTHON_RETURN_VAR); + *wrappedClass = AbstractMetaClass::findClass(classes(), returnType.typeEntry()); + } else { + QString message = QLatin1String("Invalid Argument index (0, return value) on function modification: ") + + funcType.name() + QLatin1Char(' '); + if (const AbstractMetaClass *declaringClass = func->declaringClass()) + message += declaringClass->name() + QLatin1String("::"); + message += func->name() + QLatin1String("()"); + qCWarning(lcShiboken).noquote().nospace() << message; + } + } else { + int realIndex = argIndex - 1 - OverloadData::numberOfRemovedArguments(func, argIndex - 1); + AbstractMetaType argType = getTypeWithoutContainer(func->arguments().at(realIndex).type()); + + if (argType) { + *wrappedClass = AbstractMetaClass::findClass(classes(), argType.typeEntry()); + if (argIndex == 1 + && !func->isConstructor() + && OverloadData::isSingleArgument(getFunctionGroups(func->implementingClass())[func->name()])) + pyArgName = QLatin1String(PYTHON_ARG); + else + pyArgName = pythonArgsAt(argIndex - 1); + } + } + return pyArgName; +} + +static QStringList defaultExceptionHandling() +{ + static const QStringList result{ + QLatin1String("} catch (const std::exception &e) {"), + QLatin1String(" PyErr_SetString(PyExc_RuntimeError, e.what());"), + QLatin1String("} catch (...) {"), + QLatin1String(" PyErr_SetString(PyExc_RuntimeError, \"An unknown exception was caught\");"), + QLatin1String("}")}; + return result; +} + +void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *func, + const GeneratorContext &context, int maxArgs) +{ + 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) { + auto klass = func->ownerClass(); + s << INDENT << "overloadId = " + << klass->functions().indexOf(const_cast<AbstractMetaFunction *>(func)) + << ";\n"; + break; + } + } + } + + if (func->isAbstract()) { + s << INDENT << "if (Shiboken::Object::hasCppWrapper(reinterpret_cast<SbkObject *>(self))) {\n"; + { + Indentation indent(INDENT); + s << INDENT << "PyErr_SetString(PyExc_NotImplementedError, \"pure virtual method '"; + s << func->ownerClass()->name() << '.' << func->name() << "()' not implemented.\");\n"; + s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl; + } + s << INDENT << "}\n"; + } + + // Used to provide contextual information to custom code writer function. + const AbstractMetaArgument *lastArg = nullptr; + + CodeSnipList snips; + if (func->hasInjectedCode()) { + snips = func->injectedCodeSnips(); + + // Find the last argument available in the method call to provide + // the injected code writer with information to avoid invalid replacements + // on the %# variable. + if (maxArgs > 0 && maxArgs < func->arguments().size() - OverloadData::numberOfRemovedArguments(func)) { + int removedArgs = 0; + for (int i = 0; i < maxArgs + removedArgs; i++) { + lastArg = &func->arguments().at(i); + if (func->argumentRemoved(i + 1)) + removedArgs++; + } + } else if (maxArgs != 0 && !func->arguments().isEmpty()) { + lastArg = &func->arguments().constLast(); + } + + writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::TargetLangCode, func, lastArg); + } + + writeConversionRule(s, func, TypeSystem::NativeCode); + + if (!func->isUserAdded()) { + QStringList userArgs; + if (func->functionType() != AbstractMetaFunction::CopyConstructorFunction) { + int removedArgs = 0; + for (int i = 0; i < maxArgs + removedArgs; i++) { + const AbstractMetaArgument &arg = func->arguments().at(i); + bool hasConversionRule = !func->conversionRule(TypeSystem::NativeCode, + arg.argumentIndex() + 1).isEmpty(); + if (func->argumentRemoved(i + 1)) { + // If some argument with default value is removed from a + // method signature, the said value must be explicitly + // added to the method call. + removedArgs++; + + // If have conversion rules I will use this for removed args + if (hasConversionRule) + userArgs << arg.name() + QLatin1String(CONV_RULE_OUT_VAR_SUFFIX); + else if (!arg.defaultValueExpression().isEmpty()) + userArgs.append(QLatin1String(CPP_ARG_REMOVED) + QString::number(i)); + } else { + int idx = arg.argumentIndex() - removedArgs; + bool deRef = isValueTypeWithCopyConstructorOnly(arg.type()) + || isObjectTypeUsedAsValueType(arg.type()) + || (arg.type().referenceType() == LValueReference + && isWrapperType(arg.type()) && !isPointer(arg.type())); + if (hasConversionRule) { + userArgs.append(arg.name() + QLatin1String(CONV_RULE_OUT_VAR_SUFFIX)); + } else { + QString argName; + if (deRef) + argName += QLatin1Char('*'); + argName += QLatin1String(CPP_ARG) + QString::number(idx); + userArgs.append(argName); + } + } + } + + // If any argument's default value was modified the method must be called + // with this new value whenever the user doesn't pass an explicit value to it. + // Also, any unmodified default value coming after the last user specified + // argument and before the modified argument must be explicitly stated. + QStringList otherArgs; + bool otherArgsModified = false; + bool argsClear = true; + for (int i = func->arguments().size() - 1; i >= maxArgs + removedArgs; i--) { + const AbstractMetaArgument &arg = func->arguments().at(i); + const bool defValModified = arg.hasModifiedDefaultValueExpression(); + bool hasConversionRule = !func->conversionRule(TypeSystem::NativeCode, + arg.argumentIndex() + 1).isEmpty(); + if (argsClear && !defValModified && !hasConversionRule) + continue; + argsClear = false; + otherArgsModified |= defValModified || hasConversionRule || func->argumentRemoved(i + 1); + if (hasConversionRule) + otherArgs.prepend(arg.name() + QLatin1String(CONV_RULE_OUT_VAR_SUFFIX)); + else + otherArgs.prepend(QLatin1String(CPP_ARG_REMOVED) + QString::number(i)); + } + if (otherArgsModified) + userArgs << otherArgs; + } + + bool isCtor = false; + QString methodCall; + QTextStream mc(&methodCall); + QString useVAddr; + QTextStream uva(&useVAddr); + if (func->isOperatorOverload() && !func->isCallOperator()) { + QString firstArg(QLatin1Char('(')); + if (!func->isPointerOperator()) // no de-reference operator + firstArg += QLatin1Char('*'); + firstArg += QLatin1String(CPP_SELF_VAR); + firstArg += QLatin1Char(')'); + QString secondArg = QLatin1String(CPP_ARG0); + if (!func->isUnaryOperator() && shouldDereferenceArgumentPointer(func->arguments().constFirst())) { + secondArg.prepend(QLatin1String("(*")); + secondArg.append(QLatin1Char(')')); + } + + if (func->isUnaryOperator()) + std::swap(firstArg, secondArg); + + QString op = func->originalName(); + 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 << Qt::endl << INDENT << "for (int i=0; i < " << secondArg << "; i++, " << firstArg << op << ");\n"; + mc << firstArg; + } else { + mc << firstArg << ' ' << op << ' ' << secondArg; + } + } else { + mc << op << ' ' << secondArg; + } + } else if (!injectedCodeCallsCppFunction(context, func)) { + if (func->isConstructor()) { + isCtor = true; + 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() && owner->isQObject()) { + s << INDENT << "void *addr = PySide::nextQObjectMemoryAddr();\n"; + uva << "if (addr) {\n"; + { + Indentation indent(INDENT); + + uva << INDENT << "cptr = " << "new (addr) ::" + << ctorCall << ";\n" + << INDENT + << "PySide::setNextQObjectMemoryAddr(0);" + << Qt::endl; + } + uva << INDENT << "} else {\n"; + { + Indentation indent(INDENT); + + uva << INDENT << "cptr = " << "new ::" + << ctorCall << ";\n"; + } + uva << INDENT << "}\n"; + } else { + mc << "new ::" << ctorCall; + } + } + } else { + QString methodCallClassName; + if (context.forSmartPointer()) + methodCallClassName = context.preciseType().cppSignature(); + else if (func->ownerClass()) + methodCallClassName = func->ownerClass()->qualifiedCppName(); + + if (func->ownerClass()) { + if (!avoidProtectedHack() || !func->isProtected()) { + if (func->isStatic()) { + mc << "::" << methodCallClassName << "::"; + } else { + const QString selfVarCast = func->ownerClass() == func->implementingClass() + ? QLatin1String(CPP_SELF_VAR) + : QLatin1String("reinterpret_cast<") + methodCallClassName + + QLatin1String(" *>(") + QLatin1String(CPP_SELF_VAR) + QLatin1Char(')'); + if (func->isConstant()) { + if (avoidProtectedHack()) { + auto ownerClass = func->ownerClass(); + mc << "const_cast<const ::"; + if (ownerClass->hasProtectedMembers() + && !ownerClass->attributes().testFlag(AbstractMetaAttributes::FinalCppClass)) { + // PYSIDE-500: Need a special wrapper cast when inherited + const QString selfWrapCast = ownerClass == func->implementingClass() + ? QLatin1String(CPP_SELF_VAR) + : QLatin1String("reinterpret_cast<") + wrapperName(ownerClass) + + QLatin1String(" *>(") + QLatin1String(CPP_SELF_VAR) + QLatin1Char(')'); + mc << wrapperName(ownerClass); + mc << " *>(" << selfWrapCast << ")->"; + } + else { + mc << methodCallClassName; + mc << " *>(" << selfVarCast << ")->"; + } + } else { + mc << "const_cast<const ::" << methodCallClassName; + mc << " *>(" << selfVarCast << ")->"; + } + } else { + mc << selfVarCast << "->"; + } + } + + if (!func->isAbstract() && func->isVirtual()) + mc << "::%CLASS_NAME::"; + + mc << func->originalName(); + } else { + if (!func->isStatic()) { + const auto *owner = func->ownerClass(); + const bool directInheritance = context.metaClass() == owner; + mc << (directInheritance ? "static_cast" : "reinterpret_cast") + << "<::" << wrapperName(owner) << " *>(" << CPP_SELF_VAR << ")->"; + } + + if (!func->isAbstract()) + mc << (func->isProtected() ? wrapperName(func->ownerClass()) : + QLatin1String("::") + + methodCallClassName) << "::"; + mc << func->originalName() << "_protected"; + } + } else { + mc << func->originalName(); + } + mc << '(' << userArgs.join(QLatin1String(", ")) << ')'; + if (!func->isAbstract() && func->isVirtual()) { + mc.flush(); + if (!avoidProtectedHack() || !func->isProtected()) { + QString virtualCall(methodCall); + QString normalCall(methodCall); + virtualCall.replace(QLatin1String("%CLASS_NAME"), + methodCallClassName); + normalCall.remove(QLatin1String("::%CLASS_NAME::")); + methodCall.clear(); + mc << "Shiboken::Object::hasCppWrapper(reinterpret_cast<SbkObject *>(self))\n" + << INDENT << " ? " << virtualCall << '\n' + << INDENT << " : " << normalCall; + } + } + } + } + + if (!injectedCodeCallsCppFunction(context, func)) { + const bool allowThread = func->allowThread(); + const bool generateExceptionHandling = func->generateExceptionHandling(); + if (generateExceptionHandling) { + s << INDENT << "try {\n"; + ++INDENT.indent; + if (allowThread) { + s << INDENT << "Shiboken::ThreadStateSaver threadSaver;\n" + << INDENT << "threadSaver.save();\n"; + } + } else if (allowThread) { + s << INDENT << BEGIN_ALLOW_THREADS << Qt::endl; + } + s << INDENT; + if (isCtor) { + s << (useVAddr.isEmpty() ? + 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()); + if (metaEnum) { + QString enumName; + if (metaEnum->isProtected()) + enumName = protectedEnumSurrogateName(metaEnum); + else + enumName = func->type().cppSignature(); + methodCall.prepend(enumName + QLatin1Char('(')); + methodCall.append(QLatin1Char(')')); + s << enumName; + writeReturnType = false; + } + } + if (writeReturnType) { + s << func->type().cppSignature(); + if (isObjectTypeUsedAsValueType(func->type())) { + s << '*'; + methodCall.prepend(QString::fromLatin1("new %1(").arg(func->type().typeEntry()->qualifiedCppName())); + methodCall.append(QLatin1Char(')')); + } + } + s << " " << CPP_RETURN_VAR << " = "; + s << methodCall << ";\n"; + } else { + s << methodCall << ";\n"; + } + + if (allowThread) { + s << INDENT << (generateExceptionHandling + ? "threadSaver.restore();" : END_ALLOW_THREADS) << '\n'; + } + + // Convert result + if (!func->conversionRule(TypeSystem::TargetLangCode, 0).isEmpty()) { + writeConversionRule(s, func, TypeSystem::TargetLangCode, QLatin1String(PYTHON_RETURN_VAR)); + } else if (!isCtor && !func->isInplaceOperator() && !func->isVoid() + && !injectedCodeHasReturnValueAttribution(func, TypeSystem::TargetLangCode)) { + s << INDENT << PYTHON_RETURN_VAR << " = "; + if (isObjectTypeUsedAsValueType(func->type())) { + s << "Shiboken::Object::newObject(reinterpret_cast<SbkObjectType *>(" << cpythonTypeNameExt(func->type().typeEntry()) + << "), " << CPP_RETURN_VAR << ", true, true)"; + } else { + writeToPythonConversion(s, func->type(), func->ownerClass(), QLatin1String(CPP_RETURN_VAR)); + } + s << ";\n"; + } + + if (generateExceptionHandling) { // "catch" code + --INDENT.indent; + const QStringList handlingCode = defaultExceptionHandling(); + for (const auto &line : handlingCode) + s << INDENT << line << '\n'; + } + } + } + + if (func->hasInjectedCode() && !func->isConstructor()) + writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::TargetLangCode, func, lastArg); + + bool hasReturnPolicy = false; + + // Ownership transference between C++ and Python. + QVector<ArgumentModification> ownership_mods; + // Python object reference management. + QVector<ArgumentModification> refcount_mods; + const FunctionModificationList &funcMods = func->modifications(); + for (const FunctionModification &func_mod : funcMods) { + for (const ArgumentModification &arg_mod : func_mod.argument_mods) { + if (!arg_mod.ownerships.isEmpty() && arg_mod.ownerships.contains(TypeSystem::TargetLangCode)) + ownership_mods.append(arg_mod); + else if (!arg_mod.referenceCounts.isEmpty()) + refcount_mods.append(arg_mod); + } + } + + // If there's already a setParent(return, me), don't use the return heuristic! + if (func->argumentOwner(func->ownerClass(), -1).index == 0) + hasReturnPolicy = true; + + if (!ownership_mods.isEmpty()) { + 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 << ")\n" << Qt::endl; + break; + } + + if (arg_mod.index == 0 || arg_mod.owner.index == 0) + hasReturnPolicy = true; + + // The default ownership does nothing. This is useful to avoid automatic heuristically + // based generation of code defining parenting. + if (arg_mod.ownerships[TypeSystem::TargetLangCode] == TypeSystem::DefaultOwnership) + continue; + + s << INDENT << "Shiboken::Object::"; + if (arg_mod.ownerships[TypeSystem::TargetLangCode] == TypeSystem::TargetLangOwnership) { + s << "getOwnership(" << pyArgName << ");"; + } else if (wrappedClass->hasVirtualDestructor()) { + if (arg_mod.index == 0) + s << "releaseOwnership(" << PYTHON_RETURN_VAR << ");"; + else + s << "releaseOwnership(" << pyArgName << ");"; + } else { + s << "invalidate(" << pyArgName << ");"; + } + s << Qt::endl; + } + + } else if (!refcount_mods.isEmpty()) { + for (const ArgumentModification &arg_mod : qAsConst(refcount_mods)) { + ReferenceCount refCount = arg_mod.referenceCounts.constFirst(); + if (refCount.action != ReferenceCount::Set + && refCount.action != ReferenceCount::Remove + && refCount.action != ReferenceCount::Add) { + qCWarning(lcShiboken) << "\"set\", \"add\" and \"remove\" are the only values supported by Shiboken for action attribute of reference-count tag."; + continue; + } + const AbstractMetaClass *wrappedClass = nullptr; + + QString pyArgName; + if (refCount.action == ReferenceCount::Remove) { + pyArgName = QLatin1String("Py_None"); + } else { + pyArgName = argumentNameFromIndex(func, arg_mod.index, &wrappedClass); + if (pyArgName.isEmpty()) { + s << "#error Invalid reference count modification for argument " << arg_mod.index << Qt::endl << Qt::endl; + break; + } + } + + if (refCount.action == ReferenceCount::Add || refCount.action == ReferenceCount::Set) + s << INDENT << "Shiboken::Object::keepReference("; + else + s << INDENT << "Shiboken::Object::removeReference("; + + s << "reinterpret_cast<SbkObject *>(self), \""; + QString varName = arg_mod.referenceCounts.constFirst().varName; + if (varName.isEmpty()) + varName = func->minimalSignature() + QString::number(arg_mod.index); + + s << varName << "\", " << pyArgName + << (refCount.action == ReferenceCount::Add ? ", true" : "") + << ");\n"; + + if (arg_mod.index == 0) + hasReturnPolicy = true; + } + } + writeParentChildManagement(s, func, !hasReturnPolicy); +} + +QStringList CppGenerator::getAncestorMultipleInheritance(const AbstractMetaClass *metaClass) +{ + QStringList result; + const AbstractMetaClassList &baseClases = getBaseClasses(metaClass); + if (!baseClases.isEmpty()) { + for (const AbstractMetaClass *baseClass : baseClases) { + QString offset; + QTextStream(&offset) << "reinterpret_cast<uintptr_t>(static_cast<const " + << baseClass->qualifiedCppName() << " *>(class_ptr)) - base"; + result.append(offset); + offset.clear(); + QTextStream(&offset) << "reinterpret_cast<uintptr_t>(static_cast<const " + << baseClass->qualifiedCppName() << " *>(static_cast<const " + << metaClass->qualifiedCppName() + << " *>(static_cast<const void *>(class_ptr)))) - base"; + result.append(offset); + } + + for (const AbstractMetaClass *baseClass : baseClases) + result.append(getAncestorMultipleInheritance(baseClass)); + } + return result; +} + +void CppGenerator::writeMultipleInheritanceInitializerFunction(QTextStream &s, const AbstractMetaClass *metaClass) +{ + QString className = metaClass->qualifiedCppName(); + const QStringList ancestors = getAncestorMultipleInheritance(metaClass); + s << "static int mi_offsets[] = { "; + for (int i = 0; i < ancestors.size(); i++) + s << "-1, "; + 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;\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 << "));\n"; + + 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 << "}\n"; + s << INDENT << "return mi_offsets;\n"; + s << "}\n"; +} + +void CppGenerator::writeSpecialCastFunction(QTextStream &s, const AbstractMetaClass *metaClass) +{ + QString className = metaClass->qualifiedCppName(); + s << "static void * " << cpythonSpecialCastFunctionName(metaClass) << "(void *obj, SbkObjectType *desiredType)\n"; + s << "{\n"; + s << INDENT << "auto me = reinterpret_cast< ::" << className << " *>(obj);\n"; + bool firstClass = true; + const AbstractMetaClassList &allAncestors = getAllAncestors(metaClass); + for (const AbstractMetaClass *baseClass : allAncestors) { + s << INDENT << (!firstClass ? "else " : "") << "if (desiredType == reinterpret_cast<SbkObjectType *>(" << cpythonTypeNameExt(baseClass->typeEntry()) << "))\n"; + Indentation indent(INDENT); + s << INDENT << "return static_cast< ::" << baseClass->qualifiedCppName() << " *>(me);\n"; + firstClass = false; + } + s << INDENT << "return me;\n"; + s << "}\n\n"; +} + +void CppGenerator::writePrimitiveConverterInitialization(QTextStream &s, const CustomConversion *customConversion) +{ + const TypeEntry *type = customConversion->ownerType(); + QString converter = converterObject(type); + s << INDENT << "// Register converter for type '" << type->qualifiedTargetLangName() << "'.\n"; + s << INDENT << converter << " = Shiboken::Conversions::createConverter("; + if (type->targetLangApiName() == type->name()) + s << '0'; + else if (type->targetLangApiName() == QLatin1String("PyObject")) + s << "&PyBaseObject_Type"; + else + s << '&' << type->targetLangApiName() << "_Type"; + QString typeName = fixedCppTypeName(type); + s << ", " << cppToPythonFunctionName(typeName, typeName) << ");\n"; + s << INDENT << "Shiboken::Conversions::registerConverterName(" << converter << ", \"" << type->qualifiedCppName() << "\");\n"; + writeCustomConverterRegister(s, customConversion, converter); +} + +void CppGenerator::writeEnumConverterInitialization(QTextStream &s, const AbstractMetaEnum *metaEnum) +{ + if (metaEnum->isPrivate() || metaEnum->isAnonymous()) + return; + writeEnumConverterInitialization(s, metaEnum->typeEntry()); +} + +void CppGenerator::writeEnumConverterInitialization(QTextStream &s, const TypeEntry *enumType) +{ + if (!enumType) + return; + QString enumFlagName = enumType->isFlags() ? QLatin1String("flag") : QLatin1String("enum"); + QString enumPythonType = cpythonTypeNameExt(enumType); + + const FlagsTypeEntry *flags = nullptr; + if (enumType->isFlags()) + flags = static_cast<const FlagsTypeEntry *>(enumType); + + 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 << ',' << Qt::endl; + { + Indentation indent(INDENT); + s << INDENT << cppToPythonFunctionName(typeName, typeName) << ");\n"; + } + + if (flags) { + QString enumTypeName = fixedCppTypeName(flags->originator()); + QString toCpp = pythonToCppFunctionName(enumTypeName, typeName); + QString isConv = convertibleToCppFunctionName(enumTypeName, typeName); + writeAddPythonToCppConversion(s, QLatin1String("converter"), toCpp, isConv); + } + + QString toCpp = pythonToCppFunctionName(typeName, typeName); + QString isConv = convertibleToCppFunctionName(typeName, typeName); + writeAddPythonToCppConversion(s, QLatin1String("converter"), toCpp, isConv); + + if (flags) { + QString toCpp = pythonToCppFunctionName(QLatin1String("number"), typeName); + QString isConv = convertibleToCppFunctionName(QLatin1String("number"), typeName); + writeAddPythonToCppConversion(s, QLatin1String("converter"), toCpp, isConv); + } + + s << INDENT << "Shiboken::Enum::setTypeConverter(" << enumPythonType << ", converter);\n"; + + QString signature = enumType->qualifiedCppName(); + // Replace "QFlags<Class::Option>" by "Class::Options" + if (flags && signature.startsWith(QLatin1String("QFlags<")) && signature.endsWith(QLatin1Char('>'))) { + signature.chop(1); + signature.remove(0, 7); + const int lastQualifierPos = signature.lastIndexOf(QLatin1String("::")); + if (lastQualifierPos != -1) { + signature.replace(lastQualifierPos + 2, signature.size() - lastQualifierPos - 2, + flags->flagsName()); + } else { + signature = flags->flagsName(); + } + } + + while (true) { + s << INDENT << "Shiboken::Conversions::registerConverterName(converter, \"" + << signature << "\");\n"; + const int qualifierPos = signature.indexOf(QLatin1String("::")); + if (qualifierPos != -1) + signature.remove(0, qualifierPos + 2); + else + break; + } + } + s << INDENT << "}\n"; + + if (!flags) + writeEnumConverterInitialization(s, static_cast<const EnumTypeEntry *>(enumType)->flags()); +} + +void CppGenerator::writeContainerConverterInitialization(QTextStream &s, const AbstractMetaType &type) +{ + QByteArray cppSignature = QMetaObject::normalizedSignature(type.cppSignature().toUtf8()); + s << INDENT << "// Register converter for type '" << cppSignature << "'.\n"; + QString converter = converterObject(type); + s << INDENT << converter << " = Shiboken::Conversions::createConverter("; + if (type.typeEntry()->targetLangApiName() == QLatin1String("PyObject")) { + s << "&PyBaseObject_Type"; + } else { + QString baseName = cpythonBaseName(type.typeEntry()); + if (baseName == QLatin1String("PySequence")) + baseName = QLatin1String("PyList"); + s << '&' << baseName << "_Type"; + } + QString typeName = fixedCppTypeName(type); + s << ", " << cppToPythonFunctionName(typeName, typeName) << ");\n"; + QString toCpp = pythonToCppFunctionName(typeName, typeName); + QString isConv = convertibleToCppFunctionName(typeName, typeName); + 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 << "\");\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() << "]\n"; + 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() << '.' << Qt::endl; + for (const AbstractMetaClass *sourceClass : conversions) { + const QString converterVar = QLatin1String("reinterpret_cast<SbkObjectType *>(") + + cppApiVariableName(externalType->targetLangPackage()) + QLatin1Char('[') + + getTypeIndexVariableName(externalType) + QLatin1String("])"); + QString sourceTypeName = fixedCppTypeName(sourceClass->typeEntry()); + QString targetTypeName = fixedCppTypeName(externalType); + QString toCpp = pythonToCppFunctionName(sourceTypeName, targetTypeName); + QString isConv = convertibleToCppFunctionName(sourceTypeName, targetTypeName); + writeAddPythonToCppConversion(s, converterVar, toCpp, isConv); + } +} + +QString CppGenerator::multipleInheritanceInitializerFunctionName(const AbstractMetaClass *metaClass) +{ + return cpythonBaseName(metaClass->typeEntry()) + QLatin1String("_mi_init"); +} + +bool CppGenerator::supportsMappingProtocol(const AbstractMetaClass *metaClass) +{ + for (auto it = m_mappingProtocol.cbegin(), end = m_mappingProtocol.cend(); it != end; ++it) { + if (metaClass->hasFunction(it.key())) + return true; + } + + return false; +} + +bool CppGenerator::supportsNumberProtocol(const AbstractMetaClass *metaClass) +{ + return metaClass->hasArithmeticOperatorOverload() + || metaClass->hasLogicalOperatorOverload() + || metaClass->hasBitwiseOperatorOverload() + || hasBoolCast(metaClass); +} + +bool CppGenerator::supportsSequenceProtocol(const AbstractMetaClass *metaClass) +{ + for (auto it = m_sequenceProtocol.cbegin(), end = m_sequenceProtocol.cend(); it != end; ++it) { + if (metaClass->hasFunction(it.key())) + return true; + } + + const ComplexTypeEntry *baseType = metaClass->typeEntry()->baseContainerType(); + return baseType && baseType->isContainer(); +} + +bool CppGenerator::shouldGenerateGetSetList(const AbstractMetaClass *metaClass) +{ + const AbstractMetaFieldList &fields = metaClass->fields(); + for (const AbstractMetaField *f : fields) { + 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; +} + +struct pyTypeSlotEntry +{ + explicit pyTypeSlotEntry(const char *name, const QString &function) : + m_name(name), m_function(function) {} + + const char *m_name; + const QString &m_function; +}; + +QTextStream &operator<<(QTextStream &str, const pyTypeSlotEntry &e) +{ + str << '{' << e.m_name << ','; + const int padding = qMax(0, 18 - int(strlen(e.m_name))); + for (int p = 0; p < padding; ++p) + str << ' '; + if (e.m_function.isEmpty()) + str << NULL_PTR; + else + str << "reinterpret_cast<void *>(" << e.m_function << ')'; + str << "},\n"; + return str; +} + +void CppGenerator::writeClassDefinition(QTextStream &s, + const AbstractMetaClass *metaClass, + const GeneratorContext &classContext) +{ + QString tp_flags; + QString tp_init; + QString tp_new; + QString tp_dealloc; + QString tp_hash; + QString tp_call; + QString cppClassName = metaClass->qualifiedCppName(); + const QString className = chopType(cpythonTypeName(metaClass)); + QString baseClassName; + AbstractMetaFunctionList ctors; + const AbstractMetaFunctionList &allCtors = metaClass->queryFunctions(AbstractMetaClass::Constructors); + for (AbstractMetaFunction *f : allCtors) { + if (!f->isPrivate() && !f->isModifiedRemoved() && !classContext.forSmartPointer()) + ctors.append(f); + } + + if (!metaClass->baseClass()) + baseClassName = QLatin1String("reinterpret_cast<PyTypeObject *>(SbkObject_TypeF())"); + + bool onlyPrivCtor = !metaClass->hasNonPrivateConstructor(); + + const AbstractMetaClass *qCoreApp = AbstractMetaClass::findClass(classes(), QLatin1String("QCoreApplication")); + const bool isQApp = qCoreApp != Q_NULLPTR && metaClass->inheritsFrom(qCoreApp); + + tp_flags = QLatin1String("Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_CHECKTYPES"); + if (metaClass->isNamespace() || metaClass->hasPrivateDestructor()) { + tp_dealloc = metaClass->hasPrivateDestructor() ? + QLatin1String("SbkDeallocWrapperWithPrivateDtor") : + QLatin1String("Sbk_object_dealloc /* PYSIDE-832: Prevent replacement of \"0\" with subtype_dealloc. */"); + tp_init.clear(); + } else { + QString deallocClassName = classContext.useWrapper() + ? classContext.wrapperName() : cppClassName; + if (isQApp) + tp_dealloc = QLatin1String("&SbkDeallocQAppWrapper"); + else + tp_dealloc = QLatin1String("&SbkDeallocWrapper"); + if (!onlyPrivCtor && !ctors.isEmpty()) + tp_init = cpythonFunctionName(ctors.constFirst()); + } + + 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"); + // This is not generally possible, because PySide does not care about + // privacy the same way. This worked before the heap types were used, + // because inheritance is not really checked for static types. + // Instead, we check this at runtime, see SbkObjectTypeTpNew. + if (metaClass->fullName().startsWith(QLatin1String("PySide2.Qt"))) { + // PYSIDE-595: No idea how to do non-inheritance correctly. + // Since that is only relevant in shiboken, I used a shortcut for + // PySide. + tp_new = QLatin1String("SbkObjectTpNew"); + } + else { + 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")); + } + + QString tp_richcompare; + if (!metaClass->isNamespace() && metaClass->hasComparisonOperatorOverload()) + tp_richcompare = cpythonBaseName(metaClass) + QLatin1String("_richcompare"); + + QString tp_getset; + if (shouldGenerateGetSetList(metaClass) && !classContext.forSmartPointer()) + tp_getset = cpythonGettersSettersDefinitionName(metaClass); + + // search for special functions + ShibokenGenerator::clearTpFuncs(); + const AbstractMetaFunctionList &funcs = metaClass->functions(); + for (AbstractMetaFunction *func : funcs) { + if (m_tpFuncs.contains(func->name())) + m_tpFuncs[func->name()] = cpythonFunctionName(func); + } + if (m_tpFuncs.value(QLatin1String("__repr__")).isEmpty() + && metaClass->hasToStringCapability()) { + m_tpFuncs[QLatin1String("__repr__")] = writeReprFunction(s, + classContext, + metaClass->toStringCapabilityIndirections()); + } + + // class or some ancestor has multiple inheritance + const AbstractMetaClass *miClass = getMultipleInheritingClass(metaClass); + if (miClass) { + if (metaClass == miClass) + writeMultipleInheritanceInitializerFunction(s, metaClass); + writeSpecialCastFunction(s, metaClass); + s << Qt::endl; + } + + s << "// Class Definition -----------------------------------------------\n"; + s << "extern \"C\" {\n"; + + if (!metaClass->typeEntry()->hashFunction().isEmpty()) + tp_hash = QLatin1Char('&') + cpythonBaseName(metaClass) + QLatin1String("_HashFunc"); + + const AbstractMetaFunction *callOp = metaClass->findFunction(QLatin1String("operator()")); + if (callOp && !callOp->isModifiedRemoved()) + tp_call = QLatin1Char('&') + cpythonFunctionName(callOp); + + QString computedClassTargetFullName; + if (!classContext.forSmartPointer()) + computedClassTargetFullName = getClassTargetFullName(metaClass); + else + computedClassTargetFullName = getClassTargetFullName(classContext.preciseType()); + + QString suffix; + if (isObjectType(metaClass)) + suffix = QLatin1String(" *"); + const QString typePtr = QLatin1String("_") + className + + QLatin1String("_Type"); + 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) + << INDENT << pyTypeSlotEntry("Py_tp_call", tp_call) + << INDENT << pyTypeSlotEntry("Py_tp_str", m_tpFuncs.value(QLatin1String("__str__"))) + << INDENT << pyTypeSlotEntry("Py_tp_getattro", tp_getattro) + << INDENT << pyTypeSlotEntry("Py_tp_setattro", tp_setattro) + << INDENT << pyTypeSlotEntry("Py_tp_traverse", className + QLatin1String("_traverse")) + << INDENT << pyTypeSlotEntry("Py_tp_clear", className + QLatin1String("_clear")) + << INDENT << pyTypeSlotEntry("Py_tp_richcompare", tp_richcompare) + << INDENT << pyTypeSlotEntry("Py_tp_iter", m_tpFuncs.value(QLatin1String("__iter__"))) + << INDENT << pyTypeSlotEntry("Py_tp_iternext", m_tpFuncs.value(QLatin1String("__next__"))) + << INDENT << pyTypeSlotEntry("Py_tp_methods", className + QLatin1String("_methods")) + << INDENT << pyTypeSlotEntry("Py_tp_getset", tp_getset) + << INDENT << pyTypeSlotEntry("Py_tp_init", tp_init) + << INDENT << pyTypeSlotEntry("Py_tp_new", tp_new); + if (supportsSequenceProtocol(metaClass)) { + s << INDENT << "// type supports sequence protocol\n"; + writeTypeAsSequenceDefinition(s, metaClass); + } + if (supportsMappingProtocol(metaClass)) { + 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\n"; + writeTypeAsNumberDefinition(s, metaClass); + } + 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, + const GeneratorContext &context) +{ + for (auto it = m_mappingProtocol.cbegin(), end = m_mappingProtocol.cend(); it != end; ++it) { + const AbstractMetaFunction *func = metaClass->findFunction(it.key()); + if (!func) + continue; + QString funcName = cpythonFunctionName(func); + QString funcArgs = it.value().first; + QString funcRetVal = it.value().second; + + CodeSnipList snips = func->injectedCodeSnips(TypeSystem::CodeSnipPositionAny, TypeSystem::TargetLangCode); + 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<< "}\n\n"; + } +} + +void CppGenerator::writeSequenceMethods(QTextStream &s, + const AbstractMetaClass *metaClass, + const GeneratorContext &context) +{ + bool injectedCode = false; + + for (auto it = m_sequenceProtocol.cbegin(), end = m_sequenceProtocol.cend(); it != end; ++it) { + const AbstractMetaFunction *func = metaClass->findFunction(it.key()); + if (!func) + continue; + injectedCode = true; + QString funcName = cpythonFunctionName(func); + QString funcArgs = it.value().first; + QString funcRetVal = it.value().second; + + CodeSnipList snips = func->injectedCodeSnips(TypeSystem::CodeSnipPositionAny, TypeSystem::TargetLangCode); + 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<< "}\n\n"; + } + + if (!injectedCode) + writeDefaultSequenceMethods(s, context); +} + +void CppGenerator::writeTypeAsSequenceDefinition(QTextStream &s, const AbstractMetaClass *metaClass) +{ + bool hasFunctions = false; + QMap<QString, QString> funcs; + for (auto it = m_sequenceProtocol.cbegin(), end = m_sequenceProtocol.cend(); it != end; ++it) { + const QString &funcName = it.key(); + const AbstractMetaFunction *func = metaClass->findFunction(funcName); + funcs[funcName] = func ? cpythonFunctionName(func).prepend(QLatin1Char('&')) : QString(); + if (!hasFunctions && func) + hasFunctions = true; + } + + QString baseName = cpythonBaseName(metaClass); + + //use default implementation + if (!hasFunctions) { + funcs[QLatin1String("__len__")] = baseName + QLatin1String("__len__"); + funcs[QLatin1String("__getitem__")] = baseName + QLatin1String("__getitem__"); + funcs[QLatin1String("__setitem__")] = baseName + QLatin1String("__setitem__"); + } + + for (QHash<QString, QString>::const_iterator it = m_sqFuncs.cbegin(), end = m_sqFuncs.cend(); it != end; ++it) { + const QString &sqName = it.key(); + if (funcs[sqName].isEmpty()) + continue; + s << INDENT << "{Py_" << it.value() << ", (void *)" << funcs[sqName] << "},\n"; + } +} + +void CppGenerator::writeTypeAsMappingDefinition(QTextStream &s, const AbstractMetaClass *metaClass) +{ + bool hasFunctions = false; + QMap<QString, QString> funcs; + for (auto it = m_mappingProtocol.cbegin(), end = m_mappingProtocol.cend(); it != end; ++it) { + const QString &funcName = it.key(); + const AbstractMetaFunction *func = metaClass->findFunction(funcName); + funcs[funcName] = func ? cpythonFunctionName(func).prepend(QLatin1Char('&')) : QLatin1String("0"); + if (!hasFunctions && func) + hasFunctions = true; + } + + //use default implementation + if (!hasFunctions) { + funcs.insert(QLatin1String("__mlen__"), QString()); + funcs.insert(QLatin1String("__mgetitem__"), QString()); + funcs.insert(QLatin1String("__msetitem__"), QString()); + } + + for (auto it = m_mpFuncs.cbegin(), end = m_mpFuncs.cend(); it != end; ++it) { + const QString &mpName = it.key(); + if (funcs[mpName].isEmpty()) + continue; + s << INDENT << "{Py_" << it.value() << ", (void *)" << funcs[mpName] << "},\n"; + } +} + +void CppGenerator::writeTypeAsNumberDefinition(QTextStream &s, const AbstractMetaClass *metaClass) +{ + QMap<QString, QString> nb; + + nb.insert(QLatin1String("__add__"), QString()); + nb.insert(QLatin1String("__sub__"), QString()); + nb.insert(QLatin1String("__mul__"), QString()); + nb.insert(QLatin1String("__div__"), QString()); + nb.insert(QLatin1String("__mod__"), QString()); + nb.insert(QLatin1String("__neg__"), QString()); + nb.insert(QLatin1String("__pos__"), QString()); + nb.insert(QLatin1String("__invert__"), QString()); + nb.insert(QLatin1String("__lshift__"), QString()); + nb.insert(QLatin1String("__rshift__"), QString()); + nb.insert(QLatin1String("__and__"), QString()); + nb.insert(QLatin1String("__xor__"), QString()); + nb.insert(QLatin1String("__or__"), QString()); + nb.insert(QLatin1String("__iadd__"), QString()); + nb.insert(QLatin1String("__isub__"), QString()); + nb.insert(QLatin1String("__imul__"), QString()); + nb.insert(QLatin1String("__idiv__"), QString()); + nb.insert(QLatin1String("__imod__"), QString()); + nb.insert(QLatin1String("__ilshift__"), QString()); + nb.insert(QLatin1String("__irshift__"), QString()); + nb.insert(QLatin1String("__iand__"), QString()); + nb.insert(QLatin1String("__ixor__"), QString()); + nb.insert(QLatin1String("__ior__"), QString()); + + const QVector<AbstractMetaFunctionList> opOverloads = + filterGroupedOperatorFunctions(metaClass, + AbstractMetaClass::ArithmeticOp + | AbstractMetaClass::LogicalOp + | AbstractMetaClass::BitwiseOp); + + for (const AbstractMetaFunctionList &opOverload : opOverloads) { + const AbstractMetaFunction *rfunc = opOverload.at(0); + QString opName = ShibokenGenerator::pythonOperatorFunctionName(rfunc); + nb[opName] = cpythonFunctionName(rfunc); + } + + QString baseName = cpythonBaseName(metaClass); + + if (hasBoolCast(metaClass)) + nb.insert(QLatin1String("bool"), baseName + QLatin1String("___nb_bool")); + + for (QHash<QString, QString>::const_iterator it = m_nbFuncs.cbegin(), end = m_nbFuncs.cend(); it != end; ++it) { + const QString &nbName = it.key(); + if (nb[nbName].isEmpty()) + continue; + + if (nbName == QLatin1String("bool")) { + s << INDENT << "{Py_nb_bool, (void *)" << nb[nbName] << "},\n"; + } else { + bool excludeFromPy3K = nbName == QLatin1String("__div__") || nbName == QLatin1String("__idiv__"); + if (!excludeFromPy3K) + s << INDENT << "{Py_" << it.value() << ", (void *)" << nb[nbName] << "},\n"; + } + } + if (!nb[QLatin1String("__div__")].isEmpty()) + s << INDENT << "{Py_nb_true_divide, (void *)" << nb[QLatin1String("__div__")] << "},\n"; + + if (!nb[QLatin1String("__idiv__")].isEmpty()) { + 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"; + } +} + +void CppGenerator::writeTpTraverseFunction(QTextStream &s, const AbstractMetaClass *metaClass) +{ + QString baseName = cpythonBaseName(metaClass); + s << "static int "; + 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)\n"; + s << "{\n"; + s << INDENT << "return reinterpret_cast<PyTypeObject *>(SbkObject_TypeF())->tp_clear(self);\n"; + s << "}\n"; +} + +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)\n"; + s << "{\n"; + writeCppSelfDefinition(s, context, false, true); + QString conversionCode; + if (!context.forSmartPointer()) + conversionCode = cpythonToPythonConversionFunction(metaClass); + else + conversionCode = cpythonToPythonConversionFunction(context.preciseType()); + + s << INDENT << "PyObject *" << PYTHON_RETURN_VAR << " = " << conversionCode; + s << CPP_SELF_VAR << ");\n"; + writeFunctionReturnErrorCheckSection(s); + 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, + const GeneratorContext &context) +{ + ErrorCode errorCode(QString::fromLatin1(NULL_PTR)); + writeGetterFunctionStart(s, cpythonGetterFunctionName(metaField)); + + writeCppSelfDefinition(s, context); + + AbstractMetaType fieldType = metaField->type(); + // Force use of pointer to return internal variable memory + bool newWrapperSameObject = !fieldType.isConstant() && isWrapperType(fieldType) && !isPointer(fieldType); + + QString cppField; + if (avoidProtectedHack() && metaField->isProtected()) { + QTextStream(&cppField) << "static_cast<" + << context.wrapperName() << " *>(" + << CPP_SELF_VAR << ")->" << protectedFieldGetterName(metaField) << "()"; + } else { + cppField = QLatin1String(CPP_SELF_VAR) + QLatin1String("->") + metaField->name(); + if (newWrapperSameObject) { + cppField.prepend(QLatin1String("&(")); + cppField.append(QLatin1Char(')')); + } + } + if (isCppIntegralPrimitive(fieldType) || fieldType.isEnum()) { + s << INDENT << getFullTypeNameWithoutModifiers(fieldType) << " cppOut_local = " << cppField << ";\n"; + cppField = QLatin1String("cppOut_local"); + } else if (avoidProtectedHack() && metaField->isProtected()) { + s << INDENT << getFullTypeNameWithoutModifiers(fieldType); + if (fieldType.isContainer() || fieldType.isFlags() || fieldType.isSmartPointer()) { + s << " &"; + cppField.prepend(QLatin1Char('*')); + } else if ((!fieldType.isConstant() && !fieldType.isEnum() && !fieldType.isPrimitive()) || fieldType.indirections() == 1) { + s << " *"; + } + s << " fieldValue = " << cppField << ";\n"; + cppField = QLatin1String("fieldValue"); + } + + s << INDENT << "PyObject *pyOut = {};\n"; + if (newWrapperSameObject) { + // Special case colocated field with same address (first field in a struct) + s << INDENT << "if (reinterpret_cast<void *>(" + << cppField + << ") == reinterpret_cast<void *>(" + << CPP_SELF_VAR << ")) {\n"; + { + Indentation indent(INDENT); + s << INDENT << "pyOut = reinterpret_cast<PyObject *>(Shiboken::Object::findColocatedChild(" + << "reinterpret_cast<SbkObject *>(self), reinterpret_cast<SbkObjectType *>(" + << cpythonTypeNameExt(fieldType) + << ")));\n"; + s << INDENT << "if (pyOut) {\n"; + { + Indentation indent(INDENT); + s << INDENT << "Py_IncRef(pyOut);\n" + << INDENT << "return pyOut;\n"; + } + s << INDENT << "}\n"; + } + // Check if field wrapper has already been created. + s << INDENT << "} else if (Shiboken::BindingManager::instance().hasWrapper(" << cppField << ")) {" << "\n"; + { + Indentation indent(INDENT); + s << INDENT << "pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(" + << cppField << "));" << "\n"; + s << INDENT << "Py_IncRef(pyOut);" << "\n"; + s << INDENT << "return pyOut;" << "\n"; + } + s << INDENT << "}\n"; + // Create and register new wrapper + s << INDENT << "pyOut = "; + s << "Shiboken::Object::newObject(reinterpret_cast<SbkObjectType *>(" << cpythonTypeNameExt(fieldType) + << "), " << cppField << ", false, true);\n"; + s << INDENT << "Shiboken::Object::setParent(self, pyOut)"; + } else { + s << INDENT << "pyOut = "; + writeToPythonConversion(s, fieldType, metaField->enclosingClass(), cppField); + } + s << ";\n"; + + s << INDENT << "return pyOut;\n"; + s << "}\n"; +} + +// Write a getter for QPropertySpec +void CppGenerator::writeGetterFunction(QTextStream &s, const QPropertySpec *property, + const GeneratorContext &context) +{ + ErrorCode errorCode(0); + writeGetterFunctionStart(s, cpythonGetterFunctionName(property, context.metaClass())); + writeCppSelfDefinition(s, context); + 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 << "Py_XDECREF(pyResult);\n" + << INDENT << " return {};\n"; + } + s << INDENT << "}\n" + << INDENT << "return pyResult;\n}\n\n"; +} + +// 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 << "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, 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); + + const 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;\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 << ";\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 << ";\n"; + s << INDENT << PYTHON_TO_CPP_VAR << "(pyIn, &cppOut_ptr)"; + } + s << ";\n" << Qt::endl; + + if (isPointerToWrapperType(fieldType)) { + s << INDENT << "Shiboken::Object::keepReference(reinterpret_cast<SbkObject *>(self), \""; + s << metaField->name() << "\", pyIn);\n"; + } + + 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, 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)\n{\n"; + writeCppSelfDefinition(s, context, false, true); + writeUnusedVariableCast(s, QLatin1String(CPP_SELF_VAR)); + s << INDENT << "PyObject *" << PYTHON_RETURN_VAR << "{};\n"; + s << INDENT << "PythonToCppFunc " << PYTHON_TO_CPP_VAR << ";\n"; + writeUnusedVariableCast(s, QLatin1String(PYTHON_TO_CPP_VAR)); + s << Qt::endl; + + s << INDENT << "switch (op) {\n"; + { + Indentation indent(INDENT); + const QVector<AbstractMetaFunctionList> &groupedFuncs = filterGroupedOperatorFunctions(metaClass, AbstractMetaClass::ComparisonOp); + for (const AbstractMetaFunctionList &overloads : groupedFuncs) { + const AbstractMetaFunction *rfunc = overloads[0]; + + QString operatorId = ShibokenGenerator::pythonRichCompareOperatorId(rfunc); + s << INDENT << "case " << operatorId << ':' << Qt::endl; + + Indentation indent(INDENT); + + QString op = rfunc->originalName(); + op = op.right(op.size() - QLatin1String("operator").size()); + + int alternativeNumericTypes = 0; + for (const AbstractMetaFunction *func : overloads) { + if (!func->isStatic() && + ShibokenGenerator::isNumber(func->arguments().at(0).type().typeEntry())) + alternativeNumericTypes++; + } + + bool first = true; + OverloadData overloadData(overloads, this); + const OverloadDataList &nextOverloads = overloadData.nextOverloadData(); + for (OverloadData *od : nextOverloads) { + const AbstractMetaFunction *func = od->referenceFunction(); + if (func->isStatic()) + continue; + const AbstractMetaType argType = getArgumentType(func, 1); + if (!argType) + continue; + if (!first) { + s << " else "; + } else { + first = false; + s << INDENT; + } + s << "if ("; + writeTypeCheck(s, argType, QLatin1String(PYTHON_ARG), alternativeNumericTypes == 1 || isPyInt(argType)); + s << ") {\n"; + { + Indentation indent(INDENT); + 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(); + if (!snips.isEmpty()) { + writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionAny, + TypeSystem::TargetLangCode, func, + &func->arguments().constLast()); + generateOperatorCode = false; + } + } + if (generateOperatorCode) { + s << INDENT; + if (!func->isVoid()) + s << func->type().cppSignature() << " " << CPP_RETURN_VAR << " = "; + // expression + if (func->isPointerOperator()) + s << '&'; + s << CPP_SELF_VAR << ' ' << op << '('; + if (shouldDereferenceAbstractMetaTypePointer(argType)) + s << '*'; + s << CPP_ARG0 << ");\n"; + s << INDENT << PYTHON_RETURN_VAR << " = "; + if (!func->isVoid()) + writeToPythonConversion(s, func->type(), metaClass, QLatin1String(CPP_RETURN_VAR)); + else + s << "Py_None;\n" << INDENT << "Py_INCREF(Py_None)"; + s << ";\n"; + } + } + s << INDENT << '}'; + } + + 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") << ";\n"; + s << INDENT << "Py_INCREF(" << PYTHON_RETURN_VAR << ");\n"; + } else { + Indentation indent(INDENT); + s << INDENT << "goto " << baseName << "_RichComparison_TypeError;\n"; + } + s << INDENT<< "}\n\n"; + + s << INDENT << "break;\n"; + } + s << INDENT << "default:\n"; + { + Indentation indent(INDENT); + 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<< "}\n\n"; + + s << INDENT << "if (" << PYTHON_RETURN_VAR << " && !PyErr_Occurred())\n"; + { + Indentation indent(INDENT); + s << INDENT << "return " << PYTHON_RETURN_VAR << ";\n"; + } + 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) +{ + Q_ASSERT(!overloads.isEmpty()); + OverloadData overloadData(overloads, this); + bool usePyArgs = pythonFunctionWrapperUsesListOfArguments(overloadData); + const AbstractMetaFunction *func = overloadData.referenceFunction(); + int min = overloadData.minArgs(); + int max = overloadData.maxArgs(); + + s << '"' << func->name() << "\", reinterpret_cast<PyCFunction>(" + << cpythonFunctionName(func) << "), "; + if ((min == max) && (max < 2) && !usePyArgs) { + if (max == 0) + s << "METH_NOARGS"; + else + s << "METH_O"; + } else { + s << "METH_VARARGS"; + if (overloadData.hasArgumentWithDefaultValue()) + s << "|METH_KEYWORDS"; + } + // 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) +{ + Q_ASSERT(!overloads.isEmpty()); + const AbstractMetaFunction *func = overloads.constFirst(); + if (m_tpFuncs.contains(func->name())) + return; + + s << INDENT; + if (OverloadData::hasStaticAndInstanceFunctions(overloads)) { + s << cpythonMethodDefinitionName(func); + } else { + s << '{'; + writeMethodDefinitionEntry(s, overloads); + s << '}'; + } + s << ',' << Qt::endl; +} + +void CppGenerator::writeSignatureInfo(QTextStream &s, const AbstractMetaFunctionList &overloads) +{ + OverloadData overloadData(overloads, this); + const AbstractMetaFunction *rfunc = overloadData.referenceFunction(); + QString funcName = fullPythonFunctionName(rfunc); + + 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) { + auto metaType = arg.type(); + if (auto viewOn = metaType.viewOn()) + metaType = *viewOn; + QString strArg = metaType.pythonSignature(); + if (!arg.defaultValueExpression().isEmpty()) { + strArg += QLatin1Char('='); + QString e = arg.defaultValueExpression(); + e.replace(QLatin1String("::"), QLatin1String(".")); + strArg += e; + } + args << arg.name() + QLatin1Char(':') + strArg; + } + // mark the multiple signatures as such, to make it easier to generate different code + if (multiple) + s << idx-- << ':'; + s << funcName << '(' << args.join(QLatin1Char(',')) << ')'; + if (!f->isVoid()) + s << "->" << f->type().pythonSignature(); + s << Qt::endl; + } +} + +void CppGenerator::writeEnumsInitialization(QTextStream &s, AbstractMetaEnumList &enums) +{ + if (enums.isEmpty()) + return; + s << INDENT << "// Initialization of enums.\n\n"; + for (const AbstractMetaEnum *cppEnum : qAsConst(enums)) { + if (cppEnum->isPrivate()) + continue; + writeEnumInitialization(s, cppEnum); + } +} + +static QString mangleName(QString name) +{ + if ( name == QLatin1String("None") + || name == QLatin1String("False") + || name == QLatin1String("True")) + name += QLatin1Char('_'); + return name; +} + +void CppGenerator::writeEnumInitialization(QTextStream &s, const AbstractMetaEnum *cppEnum) +{ + const AbstractMetaClass *enclosingClass = cppEnum->targetLangEnclosingClass(); + bool hasUpperEnclosingClass = enclosingClass && enclosingClass->targetLangEnclosingClass() != nullptr; + const EnumTypeEntry *enumTypeEntry = cppEnum->typeEntry(); + QString enclosingObjectVariable; + if (enclosingClass) + enclosingObjectVariable = cpythonTypeName(enclosingClass); + else if (hasUpperEnclosingClass) + enclosingObjectVariable = QLatin1String("enclosingClass"); + else + enclosingObjectVariable = QLatin1String("module"); + + s << INDENT << "// Initialization of "; + s << (cppEnum->isAnonymous() ? "anonymous enum identified by enum value" : "enum"); + 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: + // We need 'flags->flagsName()' with the full module/class path. + QString fullPath = getClassTargetFullName(cppEnum); + fullPath.truncate(fullPath.lastIndexOf(QLatin1Char('.')) + 1); + s << INDENT << cpythonTypeNameExt(flags) << " = PySide::QFlags::create(\"" + << packageLevel << ':' << fullPath << flags->flagsName() << "\", " + << cpythonEnumName(cppEnum) << "_number_slots);\n"; + } + + enumVarTypeObj = cpythonTypeNameExt(enumTypeEntry); + + s << INDENT << enumVarTypeObj << " = Shiboken::Enum::"; + s << ((enclosingClass || hasUpperEnclosingClass) ? "createScopedEnum" : "createGlobalEnum"); + s << '(' << enclosingObjectVariable << ',' << Qt::endl; + { + Indentation indent(INDENT); + 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 << ',' << Qt::endl << INDENT << cpythonTypeNameExt(flags); + s << ");\n"; + } + s << INDENT << "if (!" << cpythonTypeNameExt(cppEnum->typeEntry()) << ")\n"; + { + Indentation indent(INDENT); + s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl << Qt::endl; + } + } + + const AbstractMetaEnumValueList &enumValues = cppEnum->values(); + for (const AbstractMetaEnumValue *enumValue : enumValues) { + if (enumTypeEntry->isEnumValueRejected(enumValue->name())) + continue; + + QString enumValueText; + if (!avoidProtectedHack() || !cppEnum->isProtected()) { + enumValueText = QLatin1String("(long) "); + if (cppEnum->enclosingClass()) + enumValueText += cppEnum->enclosingClass()->qualifiedCppName() + QLatin1String("::"); + // Fully qualify the value which is required for C++ 11 enum classes. + if (!cppEnum->isAnonymous()) + enumValueText += cppEnum->name() + QLatin1String("::"); + enumValueText += enumValue->name(); + } else { + enumValueText += enumValue->value().toString(); + } + + switch (cppEnum->enumKind()) { + case AnonymousEnum: + if (enclosingClass || hasUpperEnclosingClass) { + s << INDENT << "{\n"; + { + Indentation indent(INDENT); + 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)\n"; + { + Indentation indent(INDENT); + s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl; + } + s << INDENT << "Py_DECREF(anonEnumItem);\n"; + } + s << INDENT << "}\n"; + } else { + s << INDENT << "if (PyModule_AddIntConstant(module, \"" << mangleName(enumValue->name()) << "\", "; + s << enumValueText << ") < 0)\n"; + { + Indentation indent(INDENT); + s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl; + } + } + break; + case CEnum: { + s << INDENT << "if (!Shiboken::Enum::"; + s << ((enclosingClass || hasUpperEnclosingClass) ? "createScopedEnumItem" : "createGlobalEnumItem"); + s << '(' << enumVarTypeObj << ',' << Qt::endl; + Indentation indent(INDENT); + s << INDENT << enclosingObjectVariable << ", \"" << mangleName(enumValue->name()) << "\", "; + s << enumValueText << "))\n"; + s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl; + } + break; + case EnumClass: { + s << INDENT << "if (!Shiboken::Enum::createScopedEnumItem(" + << enumVarTypeObj << ',' << Qt::endl; + Indentation indent(INDENT); + s << INDENT << enumVarTypeObj<< ", \"" << mangleName(enumValue->name()) << "\", " + << enumValueText << "))\n" + << INDENT << returnStatement(m_currentErrorCode) << Qt::endl; + } + break; + } + } + + writeEnumConverterInitialization(s, cppEnum); + + s << INDENT << "// End of '" << cppEnum->name() << "' enum"; + if (cppEnum->typeEntry()->flags()) + s << "/flags"; + s << '.' << Qt::endl << Qt::endl; +} + +void CppGenerator::writeSignalInitialization(QTextStream &s, const AbstractMetaClass *metaClass) +{ + // Try to check something and print some warnings + const AbstractMetaFunctionList &signalFuncs = metaClass->cppSignalFunctions(); + for (const AbstractMetaFunction *cppSignal : signalFuncs) { + if (cppSignal->declaringClass() != metaClass) + continue; + const AbstractMetaArgumentList &arguments = cppSignal->arguments(); + for (const AbstractMetaArgument &arg : arguments) { + AbstractMetaType metaType = arg.type(); + const QByteArray origType = + QMetaObject::normalizedType(qPrintable(metaType.originalTypeDescription())); + const QByteArray cppSig = + QMetaObject::normalizedType(qPrintable(metaType.cppSignature())); + if ((origType != cppSig) && (!metaType.isFlags())) { + qCWarning(lcShiboken).noquote().nospace() + << "Typedef used on signal " << metaClass->qualifiedCppName() << "::" + << cppSignal->signature(); + } + } + } + + s << INDENT << "PySide::Signal::registerSignals(" << cpythonTypeName(metaClass) << ", &::" + << metaClass->qualifiedCppName() << "::staticMetaObject);\n"; +} + +void CppGenerator::writeFlagsToLong(QTextStream &s, const AbstractMetaEnum *cppEnum) +{ + FlagsTypeEntry *flagsEntry = cppEnum->typeEntry()->flags(); + if (!flagsEntry) + return; + 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);\n"; + s << INDENT << "return Shiboken::Conversions::copyToPython(Shiboken::Conversions::PrimitiveTypeConverter<int>(), &val);\n"; + s << "}\n"; +} + +void CppGenerator::writeFlagsNonZero(QTextStream &s, const AbstractMetaEnum *cppEnum) +{ + FlagsTypeEntry *flagsEntry = cppEnum->typeEntry()->flags(); + if (!flagsEntry) + return; + s << "static int " << cpythonEnumName(cppEnum) << "__nonzero(PyObject *self)\n"; + s << "{\n"; + + s << INDENT << "int val;\n"; + AbstractMetaType flagsType = buildAbstractMetaTypeFromTypeEntry(flagsEntry); + s << INDENT << cpythonToCppConversionFunction(flagsType) << "self, &val);\n"; + s << INDENT << "return val != 0;\n"; + s << "}\n"; +} + +void CppGenerator::writeFlagsMethods(QTextStream &s, const AbstractMetaEnum *cppEnum) +{ + writeFlagsBinaryOperator(s, cppEnum, QLatin1String("and"), QLatin1String("&")); + writeFlagsBinaryOperator(s, cppEnum, QLatin1String("or"), QLatin1String("|")); + writeFlagsBinaryOperator(s, cppEnum, QLatin1String("xor"), QLatin1String("^")); + + writeFlagsUnaryOperator(s, cppEnum, QLatin1String("invert"), QLatin1String("~")); + writeFlagsToLong(s, cppEnum); + writeFlagsNonZero(s, cppEnum); + + s << Qt::endl; +} + +void CppGenerator::writeFlagsNumberMethodsDefinition(QTextStream &s, const AbstractMetaEnum *cppEnum) +{ + QString cpythonName = cpythonEnumName(cppEnum); + + s << "static PyType_Slot " << cpythonName << "_number_slots[] = {\n"; + s << INDENT << "{Py_nb_bool, reinterpret_cast<void *>(" << cpythonName << "__nonzero)},\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 << 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, + const QString &pyOpName, const QString &cppOpName) +{ + FlagsTypeEntry *flagsEntry = cppEnum->typeEntry()->flags(); + Q_ASSERT(flagsEntry); + + 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;\n"; + s << INDENT << CPP_SELF_VAR << " = static_cast<::" << flagsEntry->originalName() + << ">(int(PyLong_AsLong(self)));\n"; + s << INDENT << "cppArg = static_cast<" << flagsEntry->originalName() << ">(int(PyLong_AsLong(" + << PYTHON_ARG << ")));\n\n"; + s << INDENT << "cppResult = " << CPP_SELF_VAR << " " << cppOpName << " cppArg;\n"; + s << INDENT << "return "; + writeToPythonConversion(s, flagsType, nullptr, QLatin1String("cppResult")); + s << ";\n"; + s<< "}\n\n"; +} + +void CppGenerator::writeFlagsUnaryOperator(QTextStream &s, const AbstractMetaEnum *cppEnum, + const QString &pyOpName, + const QString &cppOpName, bool boolResult) +{ + FlagsTypeEntry *flagsEntry = cppEnum->typeEntry()->flags(); + Q_ASSERT(flagsEntry); + + s << "PyObject *" << cpythonEnumName(cppEnum) << "___" << pyOpName + << "__(PyObject *self, PyObject *" << PYTHON_ARG << ")\n{\n"; + + AbstractMetaType flagsType = buildAbstractMetaTypeFromTypeEntry(flagsEntry); + 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 << ";\n"; + s << INDENT << "return "; + if (boolResult) + s << "PyBool_FromLong(cppResult)"; + else + writeToPythonConversion(s, flagsType, nullptr, QLatin1String("cppResult")); + s << ";\n"; + s<< "}\n\n"; +} + +QString CppGenerator::getSimpleClassInitFunctionName(const AbstractMetaClass *metaClass) const +{ + QString initFunctionName; + // Disambiguate namespaces per module to allow for extending them. + if (metaClass->isNamespace()) + initFunctionName += moduleName(); + initFunctionName += metaClass->qualifiedCppName(); + initFunctionName.replace(QLatin1String("::"), QLatin1String("_")); + return initFunctionName; +} + +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, + const GeneratorContext &classContext, + QTextStream &signatureStream) +{ + const ComplexTypeEntry *classTypeEntry = metaClass->typeEntry(); + + 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. + writeSignatureStrings(s, signatureStream, initFunctionName, "functions"); + s << "void init_" << initFunctionName; + 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() << ',' << Qt::endl; + Indentation indent(INDENT); + for (int i = 0, size = baseClasses.size(); i < size; ++i) { + if (i) + s << ",\n"; + s << INDENT << "reinterpret_cast<PyObject *>(" + << cpythonTypeNameExt(baseClasses.at(i)->typeEntry()) << ')'; + } + 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(\n"; + { + Indentation indent(INDENT); + // 1:enclosingObject + s << INDENT << enclosingObjectVariable << ",\n"; + QString typeName; + if (!classContext.forSmartPointer()) + typeName = metaClass->name(); + else + typeName = classContext.preciseType().cppSignature(); + + // 2:typeName + s << INDENT << "\"" << typeName << "\",\n"; + + // 3:originalName + s << INDENT << "\""; + if (!classContext.forSmartPointer()) { + s << metaClass->qualifiedCppName(); + if (isObjectType(classTypeEntry)) + s << '*'; + } else { + s << classContext.preciseType().cppSignature(); + } + + s << "\",\n"; + // 4:typeSpec + s << INDENT << '&' << chopType(pyTypeName) << "_spec,\n"; + + // 5:cppObjDtor + s << INDENT; + if (!metaClass->isNamespace() && !metaClass->hasPrivateDestructor()) { + QString dtorClassName = metaClass->qualifiedCppName(); + if (((avoidProtectedHack() && metaClass->hasProtectedDestructor()) || classTypeEntry->isValue()) + && classContext.useWrapper()) { + dtorClassName = classContext.wrapperName(); + } + if (classContext.forSmartPointer()) + dtorClassName = classContext.smartPointerWrapperName(); + + s << "&Shiboken::callCppDestructor< ::" << dtorClassName << " >,\n"; + } else { + s << "0,\n"; + } + + // 6:baseType: Find a type that is not disabled. + auto base = metaClass->isNamespace() + ? metaClass->extendedNamespace() : metaClass->baseClass(); + if (!metaClass->isNamespace()) { + for (; base != nullptr; base = base->baseClass()) { + const auto ct = base->typeEntry()->codeGeneration(); + if (ct == TypeEntry::GenerateCode || ct == TypeEntry::GenerateForSubclass) + break; + } + } + if (base) { + s << INDENT << "reinterpret_cast<SbkObjectType *>(" + << cpythonTypeNameExt(base->typeEntry()) << "),\n"; + } else { + s << INDENT << "0,\n"; + } + + // 7:baseTypes + if (metaClass->baseClassNames().size() > 1) + s << INDENT << pyTypeBasesVariable << ',' << Qt::endl; + else + s << INDENT << "0,\n"; + + // 8:wrapperflags + QByteArrayList wrapperFlags; + if (enc) + wrapperFlags.append(QByteArrayLiteral("Shiboken::ObjectType::WrapperFlags::InnerClass")); + if (metaClass->deleteInMainThread()) + wrapperFlags.append(QByteArrayLiteral("Shiboken::ObjectType::WrapperFlags::DeleteInMainThread")); + if (wrapperFlags.isEmpty()) + s << INDENT << '0'; + else + s << INDENT << wrapperFlags.join(" | "); + } + s << INDENT << ");\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) << Qt::endl; + else + 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 << Qt::endl; + + // class inject-code target/beginning + if (!classTypeEntry->codeSnips().isEmpty()) { + writeClassCodeSnips(s, classTypeEntry->codeSnips(), + TypeSystem::CodeSnipPositionBeginning, TypeSystem::TargetLangCode, + classContext); + s << Qt::endl; + } + + // Fill multiple inheritance data, if needed. + const AbstractMetaClass *miClass = getMultipleInheritingClass(metaClass); + if (miClass) { + s << INDENT << "MultipleInheritanceInitFunction func = "; + if (miClass == metaClass) { + s << multipleInheritanceInitializerFunctionName(miClass) << ";\n"; + } else { + s << "Shiboken::ObjectType::getMultipleInheritanceFunction(reinterpret_cast<SbkObjectType *>("; + s << cpythonTypeNameExt(miClass->typeEntry()) << "));\n"; + } + s << INDENT << "Shiboken::ObjectType::setMultipleInheritanceFunction("; + s << cpythonTypeName(metaClass) << ", func);\n"; + s << INDENT << "Shiboken::ObjectType::setCastFunction(" << cpythonTypeName(metaClass); + 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);\n\n"; + } + + AbstractMetaEnumList classEnums = metaClass->enums(); + metaClass->getEnumsFromInvisibleNamespacesToBeGenerated(&classEnums); + + ErrorCode errorCode(QString::fromLatin1("")); + writeEnumsInitialization(s, classEnums); + + if (metaClass->hasSignals()) + writeSignalInitialization(s, metaClass); + + // Write static fields + const AbstractMetaFieldList &fields = metaClass->fields(); + for (const AbstractMetaField *field : fields) { + if (!field->isStatic()) + continue; + 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 << ");\n"; + } + s << Qt::endl; + + // class inject-code target/end + if (!classTypeEntry->codeSnips().isEmpty()) { + s << Qt::endl; + writeClassCodeSnips(s, classTypeEntry->codeSnips(), + TypeSystem::CodeSnipPositionEnd, TypeSystem::TargetLangCode, + classContext); + } + + if (usePySideExtensions()) { + 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);\n"; + s << INDENT << "PySide::initDynamicMetaObject(" << pyTypeName << ", &::" << metaClass->qualifiedCppName() + << "::staticMetaObject, sizeof("; + if (shouldGenerateCppWrapper(metaClass)) + s << wrapperName(metaClass); + else + s << "::" << metaClass->qualifiedCppName(); + s << "));\n"; + } + + s << "}\n"; +} + +void CppGenerator::writeInitQtMetaTypeFunctionBody(QTextStream &s, const GeneratorContext &context) const +{ + const AbstractMetaClass *metaClass = context.metaClass(); + // Gets all class name variants used on different possible scopes + QStringList nameVariants; + if (!context.forSmartPointer()) + nameVariants << metaClass->name(); + else + nameVariants << context.preciseType().cppSignature(); + + const AbstractMetaClass *enclosingClass = metaClass->enclosingClass(); + while (enclosingClass) { + if (enclosingClass->typeEntry()->generateCode()) + nameVariants << (enclosingClass->name() + QLatin1String("::") + nameVariants.constLast()); + enclosingClass = enclosingClass->enclosingClass(); + } + + QString className; + if (!context.forSmartPointer()) + className = metaClass->qualifiedCppName(); + else + className = context.preciseType().cppSignature(); + + if (!metaClass->isNamespace() && !metaClass->isAbstract()) { + // Qt metatypes are registered only on their first use, so we do this now. + bool canBeValue = false; + if (!isObjectType(metaClass)) { + // check if there's a empty ctor + const AbstractMetaFunctionList &funcs = metaClass->functions(); + for (AbstractMetaFunction *func : funcs) { + if (func->isConstructor() && !func->arguments().count()) { + canBeValue = true; + break; + } + } + } + + if (canBeValue) { + for (const QString &name : qAsConst(nameVariants)) { + if (name == QLatin1String("iterator")) { + qCWarning(lcShiboken).noquote().nospace() + << QString::fromLatin1("%1:%2 FIXME:\n" + " The code tried to qRegisterMetaType the unqualified name " + "'iterator'. This is currently fixed by a hack(ct) and needs improvement!") + .arg(QFile::decodeName(__FILE__)).arg(__LINE__); + continue; + } + s << INDENT << "qRegisterMetaType< ::" << className << " >(\"" << name << "\");\n"; + } + } + } + + 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() << "\");\n"; + + if (metaEnum->typeEntry()->flags()) { + QString n = metaEnum->typeEntry()->flags()->originalName(); + s << INDENT << "qRegisterMetaType< ::" << n << " >(\"" << n << "\");\n"; + } + } + } +} + +void CppGenerator::writeTypeDiscoveryFunction(QTextStream &s, const AbstractMetaClass *metaClass) +{ + QString polymorphicExpr = metaClass->typeEntry()->polymorphicIdValue(); + + 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 << ")\n"; + { + Indentation indent(INDENT); + s << INDENT << "return cptr;\n"; + } + } else if (metaClass->isPolymorphic()) { + const AbstractMetaClassList &ancestors = getAllAncestors(metaClass); + for (AbstractMetaClass *ancestor : ancestors) { + if (ancestor->baseClass()) + continue; + if (ancestor->isPolymorphic()) { + s << INDENT << "if (instanceType == reinterpret_cast<SbkObjectType *>(Shiboken::SbkType< ::" + << ancestor->qualifiedCppName() << " >()))\n"; + Indentation indent(INDENT); + s << INDENT << "return dynamic_cast< ::" << metaClass->qualifiedCppName() + << " *>(reinterpret_cast< ::"<< ancestor->qualifiedCppName() << " *>(cptr));\n"; + } else { + qCWarning(lcShiboken).noquote().nospace() + << metaClass->qualifiedCppName() << " inherits from a non polymorphic type (" + << ancestor->qualifiedCppName() << "), type discovery based on RTTI is " + "impossible, write a polymorphic-id-expression for this type."; + } + + } + } + s << INDENT << "return {};\n"; + s << "}\n\n"; +} + +QString CppGenerator::writeSmartPointerGetterCast() +{ + return QLatin1String("const_cast<char *>(") + + QLatin1String(SMART_POINTER_GETTER) + QLatin1Char(')'); +} + +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(); + 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);\n"; + } + + if (attroCheck.testFlag(AttroCheckFlag::SetattroUser)) { + auto func = AbstractMetaClass::queryFirstFunction(metaClass->functions(), + AbstractMetaClass::SetAttroFunction); + Q_ASSERT(func); + s << INDENT << "{\n"; + { + Indentation indent(INDENT); + s << INDENT << "auto " << CPP_SELF_VAR << " = " + << cpythonWrapperCPtr(metaClass, QLatin1String("self")) << ";\n"; + writeClassCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionAny, + TypeSystem::TargetLangCode, context); + } + 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); +} + +void CppGenerator::writeGetattroDefinition(QTextStream &s, const AbstractMetaClass *metaClass) +{ + s << "static PyObject *" << cpythonGetattroFunctionName(metaClass) + << "(PyObject *self, PyObject *name)\n{\n"; +} + +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, AttroCheck attroCheck, + const GeneratorContext &context) +{ + Q_ASSERT(!context.forSmartPointer()); + const AbstractMetaClass *metaClass = context.metaClass(); + writeGetattroDefinition(s, metaClass); + + // PYSIDE-1019: Switch tp_dict before doing tp_getattro. + if (usePySideExtensions()) + s << INDENT << "PySide::Feature::Select(self);\n"; + + 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 << "if (auto meth = PyDict_GetItem(ob_dict, name)) {\n"; + { + Indentation indent(INDENT); + s << INDENT << "Py_INCREF(meth);\n"; + s << INDENT << "return meth;\n"; + } + 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); + s << INDENT << "return PyFunction_Check(meth) ? SBK_PyMethod_New(meth, self) : " << getattrFunc << ";\n"; + } + } + s << INDENT << "}\n"; + + 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 << 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"; + } + } + + if (attroCheck.testFlag(AttroCheckFlag::GetattroUser)) { + auto func = AbstractMetaClass::queryFirstFunction(metaClass->functions(), + AbstractMetaClass::GetAttroFunction); + Q_ASSERT(func); + s << INDENT << "{\n"; + { + Indentation indent(INDENT); + s << INDENT << "auto " << CPP_SELF_VAR << " = " + << cpythonWrapperCPtr(metaClass, QLatin1String("self")) << ";\n"; + writeClassCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionAny, + TypeSystem::TargetLangCode, context); + } + 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 << "tmp = attribute;\n"; + } + 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 { + callStr << "(module);\n"; + } +} + +bool CppGenerator::finishGeneration() +{ + //Generate CPython wrapper file + QString classInitDecl; + QTextStream s_classInitDecl(&classInitDecl); + QString classPythonDefines; + QTextStream s_classPythonDefines(&classPythonDefines); + + QSet<Include> includes; + QString globalFunctionImpl; + QTextStream s_globalFunctionImpl(&globalFunctionImpl); + QString globalFunctionDecl; + QTextStream s_globalFunctionDef(&globalFunctionDecl); + QString signaturesString; + QTextStream signatureStream(&signaturesString); + + Indentation indentation(INDENT); + + const auto functionGroups = getGlobalFunctionGroups(); + for (auto it = functionGroups.cbegin(), end = functionGroups.cend(); it != end; ++it) { + AbstractMetaFunctionList overloads; + for (AbstractMetaFunction *func : it.value()) { + if (!func->isModifiedRemoved()) { + overloads.append(func); + if (func->typeEntry()) + includes << func->typeEntry()->include(); + } + } + + if (overloads.isEmpty()) + continue; + + // Dummy context to satisfy the API. + GeneratorContext classContext; + writeMethodWrapper(s_globalFunctionImpl, overloads, classContext); + writeSignatureInfo(signatureStream, overloads); + writeMethodDefinition(s_globalFunctionDef, overloads); + } + + //this is a temporary solution before new type revison implementation + //We need move QMetaObject register before QObject + Dependencies additionalDependencies; + const AbstractMetaClassList &allClasses = classes(); + if (auto qObjectClass = AbstractMetaClass::findClass(allClasses, qObjectT())) { + if (auto qMetaObjectClass = AbstractMetaClass::findClass(allClasses, qMetaObjectT())) { + Dependency dependency; + dependency.parent = qMetaObjectClass; + dependency.child = qObjectClass; + additionalDependencies.append(dependency); + } + } + const AbstractMetaClassList lst = classesTopologicalSorted(additionalDependencies); + + for (const AbstractMetaClass *cls : lst){ + if (shouldGenerate(cls)) { + writeInitFunc(s_classInitDecl, s_classPythonDefines, INDENT, + getSimpleClassInitFunctionName(cls), + cls->typeEntry()->targetLangEnclosingEntry()); + } + } + + // Initialize smart pointer types. + const auto &smartPtrs = instantiatedSmartPointers(); + for (const AbstractMetaType &metaType : smartPtrs) { + GeneratorContext context = contextForSmartPointer(nullptr, metaType); + writeInitFunc(s_classInitDecl, s_classPythonDefines, INDENT, + getInitFunctionName(context), + metaType.typeEntry()->targetLangEnclosingEntry()); + } + + QString moduleFileName(outputDirectory() + QLatin1Char('/') + subDirectoryForPackage(packageName())); + moduleFileName += QLatin1Char('/') + moduleName().toLower() + QLatin1String("_module_wrapper.cpp"); + + + verifyDirectoryFor(moduleFileName); + FileOut file(moduleFileName); + + QTextStream &s = file.stream; + + // write license comment + s << licenseComment() << Qt::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>\n"; + s << "#include <pysideqenum.h>\n"; + s << "#include <feature_select.h>\n"; + s << "#include <qapp_macro.h>\n"; + } + + s << "#include \"" << getModuleHeaderFileName() << '"' << Qt::endl << Qt::endl; + for (const Include &include : qAsConst(includes)) + s << include; + s << Qt::endl; + + // Global enums + AbstractMetaEnumList globalEnums = this->globalEnums(); + for (const AbstractMetaClass *nsp : invisibleTopNamespaces()) + nsp->getEnumsToBeGenerated(&globalEnums); + + TypeDatabase *typeDb = TypeDatabase::instance(); + const TypeSystemTypeEntry *moduleEntry = typeDb->defaultTypeSystemType(); + Q_ASSERT(moduleEntry); + + //Extra includes + 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 << Qt::endl; + + s << "// Current module's type array.\n"; + s << "PyTypeObject **" << cppApiVariableName() << " = nullptr;\n"; + + s << "// Current module's PyObject pointer.\n"; + s << "PyObject *" << pythonModuleObjectName() << " = nullptr;\n"; + + 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()) + writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::NativeCode); + + // cleanup staticMetaObject attribute + if (usePySideExtensions()) { + s << "void cleanTypesAttributes(void) {\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 << "------------------------------------------------------------\n"; + s << globalFunctionImpl << Qt::endl; + + s << "static PyMethodDef " << moduleName() << "_methods[] = {\n"; + s << globalFunctionDecl; + s << INDENT << "{0} // Sentinel\n" << "};\n\n"; + + s << "// Classes initialization functions "; + s << "------------------------------------------------------------\n"; + s << classInitDecl << Qt::endl; + + if (!globalEnums.isEmpty()) { + QString converterImpl; + QTextStream convImpl(&converterImpl); + + s << "// Enum definitions "; + s << "------------------------------------------------------------\n"; + for (const AbstractMetaEnum *cppEnum : qAsConst(globalEnums)) { + if (cppEnum->isAnonymous() || cppEnum->isPrivate()) + continue; + writeEnumConverterFunctions(s, cppEnum); + s << Qt::endl; + } + + if (!converterImpl.isEmpty()) { + s << "// Enum converters "; + 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.\n"; + for (const QString &requiredModule : requiredModules) { + s << "PyTypeObject **" << cppApiVariableName(requiredModule) << ";\n"; + s << "SbkConverter **" << convertersVariableName(requiredModule) << ";\n"; + } + s << Qt::endl; + + s << "// Module initialization "; + s << "------------------------------------------------------------\n"; + ExtendedConverterData extendedConverters = getExtendedConverters(); + if (!extendedConverters.isEmpty()) { + 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() << '.' << Qt::endl; + for (const AbstractMetaClass *sourceClass : it.value()) { + AbstractMetaType sourceType = buildAbstractMetaTypeFromAbstractMetaClass(sourceClass); + AbstractMetaType targetType = buildAbstractMetaTypeFromTypeEntry(externalType); + writePythonToCppConversionFunctions(s, sourceType, targetType); + } + } + } + + const QVector<const CustomConversion *> &typeConversions = getPrimitiveCustomConversions(); + if (!typeConversions.isEmpty()) { + s << Qt::endl << "// Primitive Type converters.\n\n"; + for (const CustomConversion *conversion : typeConversions) { + s << "// C++ to Python conversion for type '" << conversion->ownerType()->qualifiedCppName() << "'.\n"; + writeCppToPythonFunction(s, conversion); + writeCustomConverterFunctions(s, conversion); + } + s << Qt::endl; + } + + const auto &containers = instantiatedContainers(); + if (!containers.isEmpty()) { + s << "// Container Type converters.\n\n"; + for (const AbstractMetaType &container : containers) { + s << "// C++ to Python conversion for type '" << container.cppSignature() << "'.\n"; + writeContainerConverterFunctions(s, container); + } + 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 << "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"; + + // PYSIDE-510: Create a signatures string for the introspection feature. + writeSignatureStrings(s, signatureStream, moduleName(), "global functions"); + + s << "extern \"C\" LIBSHIBOKEN_EXPORT PyObject *PyInit_" + << moduleName() << "()\n{\n"; + + ErrorCode errorCode(QLatin1String("nullptr")); + // module inject-code target/beginning + if (!snips.isEmpty()) + writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::TargetLangCode); + + for (const QString &requiredModule : requiredModules) { + s << INDENT << "{\n" << indent(INDENT) + << INDENT << "Shiboken::AutoDecRef requiredModule(Shiboken::Module::import(\"" << requiredModule << "\"));\n" + << INDENT << "if (requiredModule.isNull())\n" << indent(INDENT) + << INDENT << "return nullptr;\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.\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.\n"; + s << INDENT << "static SbkConverter *sbkConverters[SBK_" << moduleName() << "_CONVERTERS_IDX_COUNT" << "];\n"; + s << INDENT << convertersVariableName() << " = sbkConverters;\n\n"; + + s << INDENT << "PyObject *module = Shiboken::Module::create(\"" << moduleName() << "\", &moduledef);\n\n"; + + s << INDENT << "// Make module available from global scope\n"; + s << INDENT << pythonModuleObjectName() << " = module;\n\n"; + + //s << INDENT << "// Initialize converters for primitive types.\n"; + //s << INDENT << "initConverters();\n\n"; + + s << INDENT << "// Initialize classes in the type system\n"; + s << classPythonDefines; + + if (!typeConversions.isEmpty()) { + s << Qt::endl; + for (const CustomConversion *conversion : typeConversions) { + writePrimitiveConverterInitialization(s, conversion); + s << Qt::endl; + } + } + + if (!containers.isEmpty()) { + s << Qt::endl; + for (const AbstractMetaType &container : containers) { + writeContainerConverterInitialization(s, container); + s << Qt::endl; + } + } + + if (!smartPointersList.isEmpty()) { + s << Qt::endl; + for (const AbstractMetaType &smartPointer : smartPointersList) { + writeSmartPointerConverterInitialization(s, smartPointer); + s << Qt::endl; + } + } + + if (!extendedConverters.isEmpty()) { + s << Qt::endl; + for (ExtendedConverterData::const_iterator it = extendedConverters.cbegin(), end = extendedConverters.cend(); it != end; ++it) { + writeExtendedConverterInitialization(s, it.key(), it.value()); + s << Qt::endl; + } + } + + writeEnumsInitialization(s, globalEnums); + + s << INDENT << "// Register primitive types converters.\n"; + const PrimitiveTypeEntryList &primitiveTypeList = primitiveTypes(); + for (const PrimitiveTypeEntry *pte : primitiveTypeList) { + if (!pte->generateCode() || !pte->isCppPrimitive()) + continue; + const TypeEntry *referencedType = pte->basicReferencedTypeEntry(); + if (!referencedType) + continue; + QString converter = converterObject(referencedType); + QStringList cppSignature = pte->qualifiedCppName().split(QLatin1String("::"), Qt::SkipEmptyParts); + while (!cppSignature.isEmpty()) { + QString signature = cppSignature.join(QLatin1String("::")); + s << INDENT << "Shiboken::Conversions::registerConverterName(" << converter << ", \"" << signature << "\");\n"; + cppSignature.removeFirst(); + } + } + + s << Qt::endl; + if (maxTypeIndex) + s << INDENT << "Shiboken::Module::registerTypes(module, " << cppApiVariableName() << ");\n"; + s << INDENT << "Shiboken::Module::registerTypeConverters(module, " << convertersVariableName() << ");\n"; + + 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()) + writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::TargetLangCode); + + // module inject-code native/end + if (!snips.isEmpty()) + writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode); + + if (usePySideExtensions()) { + for (AbstractMetaEnum *metaEnum : qAsConst(globalEnums)) + if (!metaEnum->isAnonymous()) { + s << INDENT << "qRegisterMetaType< ::" << metaEnum->typeEntry()->qualifiedCppName() << " >(\"" << metaEnum->name() << "\");\n"; + } + + // cleanup staticMetaObject attribute + s << INDENT << "PySide::registerCleanupFunction(cleanTypesAttributes);\n\n"; + } + + // finish the rest of __signature__ initialization. + s << INDENT << "FinishSignatureInitialization(module, " << moduleName() + << "_SignatureStrings);\n" + << INDENT << "\nreturn module;\n}\n"; + + return file.done() != FileOut::Failure; +} + +static ArgumentOwner getArgumentOwner(const AbstractMetaFunction *func, int argIndex) +{ + ArgumentOwner argOwner = func->argumentOwner(func->ownerClass(), argIndex); + if (argOwner.index == ArgumentOwner::InvalidIndex) + argOwner = func->argumentOwner(func->declaringClass(), argIndex); + return argOwner; +} + +bool CppGenerator::writeParentChildManagement(QTextStream &s, const AbstractMetaFunction *func, int argIndex, bool useHeuristicPolicy) +{ + const int numArgs = func->arguments().count(); + bool ctorHeuristicEnabled = func->isConstructor() && useCtorHeuristic() && useHeuristicPolicy; + + const auto &groups = func->implementingClass() + ? getFunctionGroups(func->implementingClass()) + : getGlobalFunctionGroups(); + bool usePyArgs = pythonFunctionWrapperUsesListOfArguments(OverloadData(groups[func->name()], this)); + + ArgumentOwner argOwner = getArgumentOwner(func, argIndex); + ArgumentOwner::Action action = argOwner.action; + int parentIndex = argOwner.index; + int childIndex = argIndex; + if (ctorHeuristicEnabled && argIndex > 0 && numArgs) { + const AbstractMetaArgument &arg = func->arguments().at(argIndex-1); + if (arg.name() == QLatin1String("parent") && isObjectType(arg.type())) { + action = ArgumentOwner::Add; + parentIndex = argIndex; + childIndex = -1; + } + } + + QString parentVariable; + QString childVariable; + if (action != ArgumentOwner::Invalid) { + if (!usePyArgs && argIndex > 1) + qCWarning(lcShiboken).noquote().nospace() + << "Argument index for parent tag out of bounds: " << func->signature(); + + if (action == ArgumentOwner::Remove) { + parentVariable = QLatin1String("Py_None"); + } else { + if (parentIndex == 0) { + parentVariable = QLatin1String(PYTHON_RETURN_VAR); + } else if (parentIndex == -1) { + parentVariable = QLatin1String("self"); + } else { + parentVariable = usePyArgs + ? pythonArgsAt(parentIndex - 1) : QLatin1String(PYTHON_ARG); + } + } + + if (childIndex == 0) { + childVariable = QLatin1String(PYTHON_RETURN_VAR); + } else if (childIndex == -1) { + childVariable = QLatin1String("self"); + } else { + childVariable = usePyArgs + ? pythonArgsAt(childIndex - 1) : QLatin1String(PYTHON_ARG); + } + + s << INDENT << "Shiboken::Object::setParent(" << parentVariable << ", " << childVariable << ");\n"; + return true; + } + + return false; +} + +void CppGenerator::writeParentChildManagement(QTextStream &s, const AbstractMetaFunction *func, bool useHeuristicForReturn) +{ + const int numArgs = func->arguments().count(); + + // -1 = return value + // 0 = self + // 1..n = func. args. + for (int i = -1; i <= numArgs; ++i) + writeParentChildManagement(s, func, i, useHeuristicForReturn); + + if (useHeuristicForReturn) + writeReturnValueHeuristics(s, func); +} + +void CppGenerator::writeReturnValueHeuristics(QTextStream &s, const AbstractMetaFunction *func) +{ + const AbstractMetaType &type = func->type(); + if (!useReturnValueHeuristic() + || !func->ownerClass() + || type.isVoid() + || func->isStatic() + || func->isConstructor() + || !func->typeReplaced(0).isEmpty()) { + return; + } + + 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 << ");\n"; + } +} + +void CppGenerator::writeHashFunction(QTextStream &s, const GeneratorContext &context) +{ + const AbstractMetaClass *metaClass = context.metaClass(); + const char hashType[] = "Py_hash_t"; + s << "static " << hashType << ' ' << cpythonBaseName(metaClass) + << "_HashFunc(PyObject *self) {\n"; + writeCppSelfDefinition(s, context); + s << INDENT << "return " << hashType << '(' + << metaClass->typeEntry()->hashFunction() << '(' + << (isObjectType(metaClass) ? "" : "*") << CPP_SELF_VAR << "));\n"; + s<< "}\n\n"; +} + +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)\n{\n"; + writeCppSelfDefinition(s, context); + s << INDENT << "return " << CPP_SELF_VAR << "->size();\n"; + s << "}\n"; + + // __getitem__ + s << "PyObject *" << cpythonBaseName(metaClass->typeEntry()) + << "__getitem__(PyObject *self, Py_ssize_t _i)\n{\n"; + writeCppSelfDefinition(s, context); + writeIndexError(s, QLatin1String("index out of bounds")); + + QString value; + s << INDENT << metaClass->qualifiedCppName() << "::const_iterator _item = " + << CPP_SELF_VAR << "->begin();\n" + << INDENT << "std::advance(_item, _i);\n"; + + 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 << ";\n"; + s << "}\n"; + + // __setitem__ + ErrorCode errorCode2(-1); + 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 << ";\n"; + s << INDENT << "if (!"; + writeTypeCheck(s, itemType, QLatin1String("pyArg"), isNumber(itemType.typeEntry())); + s << ") {\n"; + { + Indentation indent(INDENT); + s << INDENT << "PyErr_SetString(PyExc_TypeError, \"attributed value with wrong type, '"; + s << itemType.name() << "' or other convertible type expected\");\n"; + s << INDENT << "return -1;\n"; + } + s << INDENT << "}\n"; + writeArgumentConversion(s, itemType, QLatin1String("cppValue"), QLatin1String("pyArg"), metaClass); + + 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()) {\n"; + { + Indentation indent(INDENT); + s << INDENT << "PyErr_SetString(PyExc_IndexError, \"" << errorMsg << "\");\n"; + s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl; + } + s << INDENT << "}\n"; +} + +QString CppGenerator::writeReprFunction(QTextStream &s, + const GeneratorContext &context, + uint indirections) +{ + const AbstractMetaClass *metaClass = context.metaClass(); + QString funcName = cpythonBaseName(metaClass) + QLatin1String("__repr__"); + s << "extern \"C\"\n{\n"; + s << "static PyObject *" << funcName << "(PyObject *self)\n{\n"; + writeCppSelfDefinition(s, context); + 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 << ";\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);\n"; + } + 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, '.'))\n"; + { + Indentation indent(INDENT); + s << INDENT << "return Shiboken::String::fromFormat(\"<%s.%s at %p>\", Shiboken::String::toCString(mod), str.constData(), self);\n"; + } + s << INDENT << "else\n"; + { + Indentation indent(INDENT); + s << INDENT << "return Shiboken::String::fromFormat(\"<%s at %p>\", str.constData(), self);\n"; + } + s << "}\n"; + s << "} // extern C\n\n"; + return funcName; +} |