diff options
Diffstat (limited to 'sources/shiboken6/generator/shiboken')
-rw-r--r-- | sources/shiboken6/generator/shiboken/cppgenerator.cpp | 6444 | ||||
-rw-r--r-- | sources/shiboken6/generator/shiboken/cppgenerator.h | 419 | ||||
-rw-r--r-- | sources/shiboken6/generator/shiboken/ctypenames.h | 56 | ||||
-rw-r--r-- | sources/shiboken6/generator/shiboken/headergenerator.cpp | 661 | ||||
-rw-r--r-- | sources/shiboken6/generator/shiboken/headergenerator.h | 70 | ||||
-rw-r--r-- | sources/shiboken6/generator/shiboken/overloaddata.cpp | 1093 | ||||
-rw-r--r-- | sources/shiboken6/generator/shiboken/overloaddata.h | 164 | ||||
-rw-r--r-- | sources/shiboken6/generator/shiboken/shibokengenerator.cpp | 2884 | ||||
-rw-r--r-- | sources/shiboken6/generator/shiboken/shibokengenerator.h | 585 |
9 files changed, 12376 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; +} diff --git a/sources/shiboken6/generator/shiboken/cppgenerator.h b/sources/shiboken6/generator/shiboken/cppgenerator.h new file mode 100644 index 000000000..25d4b22db --- /dev/null +++ b/sources/shiboken6/generator/shiboken/cppgenerator.h @@ -0,0 +1,419 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef CPPGENERATOR_H +#define CPPGENERATOR_H + +#include "shibokengenerator.h" + +/** + * The CppGenerator generate the implementations of C++ bindings classes. + */ +class CppGenerator : public ShibokenGenerator +{ +public: + CppGenerator(); + + const char *name() const override { return "Source generator"; } + +protected: + QString fileNameSuffix() const override; + QString fileNameForContext(const GeneratorContext &context) const override; + QVector<AbstractMetaFunctionList> filterGroupedOperatorFunctions(const AbstractMetaClass *metaClass, + uint query); + void generateClass(QTextStream &s, const GeneratorContext &classContext) override; + bool finishGeneration() override; + +private: + void writeInitFunc(QTextStream &declStr, QTextStream &callStr, + const Indentor &indent, const QString &initFunctionName, + const TypeEntry *enclosingEntry = nullptr); + void writeCacheResetNative(QTextStream &s, const GeneratorContext &classContext); + void writeConstructorNative(QTextStream &s, const GeneratorContext &classContext, + const AbstractMetaFunction *func); + void writeDestructorNative(QTextStream &s, const GeneratorContext &classContext); + + QString getVirtualFunctionReturnTypeName(const AbstractMetaFunction *func); + void writeVirtualMethodNative(QTextStream &s, const AbstractMetaFunction *func, int cacheIndex); + void writeVirtualMethodCppCall(QTextStream &s, const AbstractMetaFunction *func, + const QString &funcName, const CodeSnipList &snips, + const AbstractMetaArgument *lastArg, const TypeEntry *retType, + const QString &returnStatement); + QString virtualMethodReturn(QTextStream &s, + const AbstractMetaFunction *func, + const FunctionModificationList &functionModifications); + void writeMetaObjectMethod(QTextStream &s, const GeneratorContext &classContext); + void writeMetaCast(QTextStream &s, const GeneratorContext &classContext); + + void writeEnumConverterFunctions(QTextStream &s, const TypeEntry *enumType); + void writeEnumConverterFunctions(QTextStream &s, const AbstractMetaEnum *metaEnum); + void writeConverterFunctions(QTextStream &s, const AbstractMetaClass *metaClass, + const GeneratorContext &classContext); + void writeCustomConverterFunctions(QTextStream &s, const CustomConversion *customConversion); + void writeConverterRegister(QTextStream &s, const AbstractMetaClass *metaClass, + const GeneratorContext &classContext); + void writeCustomConverterRegister(QTextStream &s, const CustomConversion *customConversion, const QString &converterVar); + + void writeContainerConverterFunctions(QTextStream &s, const AbstractMetaType &containerType); + + void writeSmartPointerConverterFunctions(QTextStream &s, const AbstractMetaType &smartPointerType); + + void writeMethodWrapperPreamble(QTextStream &s, OverloadData &overloadData, + const GeneratorContext &context); + void writeConstructorWrapper(QTextStream &s, const AbstractMetaFunctionList &overloads, + const GeneratorContext &classContext); + void writeMethodWrapper(QTextStream &s, const AbstractMetaFunctionList &overloads, + const GeneratorContext &classContext); + void writeArgumentsInitializer(QTextStream &s, OverloadData &overloadData); + void writeCppSelfConversion(QTextStream &s, const GeneratorContext &context, + const QString &className, bool useWrapperClass); + void writeCppSelfDefinition(QTextStream &s, + const AbstractMetaFunction *func, + const GeneratorContext &context, + bool hasStaticOverload = false); + void writeCppSelfDefinition(QTextStream &s, + const GeneratorContext &context, + bool hasStaticOverload = false, + bool cppSelfAsReference = false); + + void writeErrorSection(QTextStream &s, OverloadData &overloadData); + void writeFunctionReturnErrorCheckSection(QTextStream &s, bool hasReturnValue = true); + + /// Writes the check section for the validity of wrapped C++ objects. + void writeInvalidPyObjectCheck(QTextStream &s, const QString &pyObj); + + void writeTypeCheck(QTextStream &s, AbstractMetaType argType, const QString &argumentName, + bool isNumber = false, const QString &customType = QString(), + bool rejectNull = false); + void writeTypeCheck(QTextStream& s, const OverloadData *overloadData, QString argumentName); + + void writeTypeDiscoveryFunction(QTextStream &s, const AbstractMetaClass *metaClass); + + void writeSetattroDefinition(QTextStream &s, const AbstractMetaClass *metaClass) const; + void writeSetattroDefaultReturn(QTextStream &s) const; + void writeSmartPointerSetattroFunction(QTextStream &s, const GeneratorContext &context); + void writeSetattroFunction(QTextStream &s, AttroCheck attroCheck, + const GeneratorContext &context); + static void writeGetattroDefinition(QTextStream &s, const AbstractMetaClass *metaClass); + void writeSmartPointerGetattroFunction(QTextStream &s, const GeneratorContext &context); + void writeGetattroFunction(QTextStream &s, AttroCheck attroCheck, + const GeneratorContext &context); + QString writeSmartPointerGetterCast(); + QString qObjectGetAttroFunction() const; + + /** + * Writes Python to C++ conversions for arguments on Python wrappers. + * If implicit conversions, and thus new object allocation, are needed, + * code to deallocate a possible new instance is also generated. + * \param s text stream to write + * \param argType a pointer to the argument type to be converted + * \param argName C++ argument name + * \param pyArgName Python argument name + * \param context the current meta class + * \param defaultValue an optional default value to be used instead of the conversion result + * \param castArgumentAsUnused if true the converted argument is cast as unused to avoid compiler warnings + */ + void writeArgumentConversion(QTextStream &s, const AbstractMetaType &argType, + const QString &argName, const QString &pyArgName, + const AbstractMetaClass *context = nullptr, + const QString &defaultValue = QString(), + bool castArgumentAsUnused = false); + + /** + * Returns the AbstractMetaType for a function argument. + * If the argument type was modified in the type system, this method will + * try to build a new type based on the type name defined in the type system. + * \param func The function which owns the argument. + * \param argPos Argument position in the function signature. + * Note that the position 0 represents the return value, and the function + * parameters start counting on 1. + * \param newType It is set to true if the type returned is a new object that must be deallocated. + * \return The type of the argument indicated by \p argPos. + */ + const AbstractMetaType getArgumentType(const AbstractMetaFunction *func, int argPos); + + void writePythonToCppTypeConversion(QTextStream &s, + const AbstractMetaType &type, + const QString &pyIn, + const QString &cppOut, + const AbstractMetaClass *context = nullptr, + const QString &defaultValue = QString()); + + /// Writes the conversion rule for arguments of regular and virtual methods. + void writeConversionRule(QTextStream &s, const AbstractMetaFunction *func, TypeSystem::Language language); + /// Writes the conversion rule for the return value of a method. + void writeConversionRule(QTextStream &s, const AbstractMetaFunction *func, TypeSystem::Language language, const QString &outputVar); + + /** + * Set the Python method wrapper return value variable to Py_None if + * there are return types different from void in any of the other overloads + * for the function passed as parameter. + * \param s text stream to write + * \param func a pointer to the function that will possibly return Py_None + * \param thereIsReturnValue indicates if the return type of any of the other overloads + * for this function is different from 'void' + */ + void writeNoneReturn(QTextStream &s, const AbstractMetaFunction *func, bool thereIsReturnValue); + + /** + * Writes the Python function wrapper overload decisor that selects which C++ + * method/function to call with the received Python arguments. + * \param s text stream to write + * \param overloadData the overload data describing all the possible overloads for the function/method + */ + void writeOverloadedFunctionDecisor(QTextStream &s, const OverloadData &overloadData); + /// Recursive auxiliar method to the other writeOverloadedFunctionDecisor. + void writeOverloadedFunctionDecisorEngine(QTextStream &s, const OverloadData *parentOverloadData); + + /// Writes calls to all the possible method/function overloads. + void writeFunctionCalls(QTextStream &s, + const OverloadData &overloadData, + const GeneratorContext &context); + + /// Writes the call to a single function usually from a collection of overloads. + void writeSingleFunctionCall(QTextStream &s, + const OverloadData &overloadData, + const AbstractMetaFunction *func, + const GeneratorContext &context); + + /// Returns the name of a C++ to Python conversion function. + static QString cppToPythonFunctionName(const QString &sourceTypeName, QString targetTypeName = QString()); + + /// Returns the name of a Python to C++ conversion function. + static QString pythonToCppFunctionName(const QString &sourceTypeName, const QString &targetTypeName); + static QString pythonToCppFunctionName(const AbstractMetaType &sourceType, const AbstractMetaType &targetType); + static QString pythonToCppFunctionName(const CustomConversion::TargetToNativeConversion *toNative, const TypeEntry *targetType); + + /// Returns the name of a Python to C++ convertible check function. + static QString convertibleToCppFunctionName(const QString &sourceTypeName, const QString &targetTypeName); + static QString convertibleToCppFunctionName(const AbstractMetaType &sourceType, const AbstractMetaType &targetType); + static QString convertibleToCppFunctionName(const CustomConversion::TargetToNativeConversion *toNative, const TypeEntry *targetType); + + /// Writes a C++ to Python conversion function. + void writeCppToPythonFunction(QTextStream &s, const QString &code, const QString &sourceTypeName, QString targetTypeName = QString()); + void writeCppToPythonFunction(QTextStream &s, const CustomConversion *customConversion); + void writeCppToPythonFunction(QTextStream &s, const AbstractMetaType &containerType); + + /// Writes a Python to C++ conversion function. + void writePythonToCppFunction(QTextStream &s, const QString &code, const QString &sourceTypeName, const QString &targetTypeName); + + /// Writes a Python to C++ convertible check function. + void writeIsPythonConvertibleToCppFunction(QTextStream &s, + const QString &sourceTypeName, + const QString &targetTypeName, + const QString &condition, + QString pythonToCppFuncName = QString(), + bool acceptNoneAsCppNull = false); + + /// Writes a pair of Python to C++ conversion and check functions. + void writePythonToCppConversionFunctions(QTextStream &s, + const AbstractMetaType &sourceType, + const AbstractMetaType &targetType, + QString typeCheck = QString(), + QString conversion = QString(), + const QString &preConversion = QString()); + /// Writes a pair of Python to C++ conversion and check functions for implicit conversions. + void writePythonToCppConversionFunctions(QTextStream &s, + const CustomConversion::TargetToNativeConversion *toNative, + const TypeEntry *targetType); + + /// Writes a pair of Python to C++ conversion and check functions for instantiated container types. + void writePythonToCppConversionFunctions(QTextStream &s, const AbstractMetaType &containerType); + + void writeAddPythonToCppConversion(QTextStream &s, const QString &converterVar, const QString &pythonToCppFunc, const QString &isConvertibleFunc); + + void writeNamedArgumentResolution(QTextStream &s, const AbstractMetaFunction *func, bool usePyArgs); + + /// Returns a string containing the name of an argument for the given function and argument index. + QString argumentNameFromIndex(const AbstractMetaFunction *func, int argIndex, const AbstractMetaClass **wrappedClass); + void writeMethodCall(QTextStream &s, const AbstractMetaFunction *func, + const GeneratorContext &context, int maxArgs = 0); + + QString getInitFunctionName(const GeneratorContext &context) const; + QString getSimpleClassInitFunctionName(const AbstractMetaClass *metaClass) const; + + void writeSignatureStrings(QTextStream &s, QTextStream &signatureStream, + const QString &arrayName, + const char *comment) const; + void writeClassRegister(QTextStream &s, + const AbstractMetaClass *metaClass, + const GeneratorContext &classContext, + QTextStream &signatureStream); + void writeClassDefinition(QTextStream &s, + const AbstractMetaClass *metaClass, + const GeneratorContext &classContext); + void writeMethodDefinitionEntry(QTextStream &s, const AbstractMetaFunctionList &overloads); + void writeMethodDefinition(QTextStream &s, const AbstractMetaFunctionList &overloads); + void writeSignatureInfo(QTextStream &s, const AbstractMetaFunctionList &overloads); + /// Writes the implementation of all methods part of python sequence protocol + void writeSequenceMethods(QTextStream &s, + const AbstractMetaClass *metaClass, + const GeneratorContext &context); + void writeTypeAsSequenceDefinition(QTextStream &s, const AbstractMetaClass *metaClass); + + /// Writes the PyMappingMethods structure for types that supports the python mapping protocol. + void writeTypeAsMappingDefinition(QTextStream &s, const AbstractMetaClass *metaClass); + void writeMappingMethods(QTextStream &s, + const AbstractMetaClass *metaClass, + const GeneratorContext &context); + + void writeTypeAsNumberDefinition(QTextStream &s, const AbstractMetaClass *metaClass); + + void writeTpTraverseFunction(QTextStream &s, const AbstractMetaClass *metaClass); + void writeTpClearFunction(QTextStream &s, const AbstractMetaClass *metaClass); + + void writeCopyFunction(QTextStream &s, const GeneratorContext &context); + + void writeGetterFunction(QTextStream &s, + const AbstractMetaField *metaField, + const GeneratorContext &context); + void writeGetterFunction(QTextStream &s, + const QPropertySpec *property, + const GeneratorContext &context); + void writeSetterFunctionPreamble(QTextStream &s, + const QString &name, + const QString &funcName, + const AbstractMetaType &type, + const GeneratorContext &context); + void writeSetterFunction(QTextStream &s, + const AbstractMetaField *metaField, + const GeneratorContext &context); + void writeSetterFunction(QTextStream &s, + const QPropertySpec *property, + const GeneratorContext &context); + + void writeRichCompareFunction(QTextStream &s, const GeneratorContext &context); + + void writeEnumsInitialization(QTextStream &s, AbstractMetaEnumList &enums); + void writeEnumInitialization(QTextStream &s, const AbstractMetaEnum *metaEnum); + + void writeSignalInitialization(QTextStream &s, const AbstractMetaClass *metaClass); + + void writeFlagsMethods(QTextStream &s, const AbstractMetaEnum *cppEnum); + void writeFlagsToLong(QTextStream &s, const AbstractMetaEnum *cppEnum); + void writeFlagsNonZero(QTextStream &s, const AbstractMetaEnum *cppEnum); + void writeFlagsNumberMethodsDefinition(QTextStream &s, const AbstractMetaEnum *cppEnum); + void writeFlagsNumberMethodsDefinitions(QTextStream &s, const AbstractMetaEnumList &enums); + void writeFlagsBinaryOperator(QTextStream &s, const AbstractMetaEnum *cppEnum, + const QString &pyOpName, const QString &cppOpName); + void writeFlagsUnaryOperator(QTextStream &s, const AbstractMetaEnum *cppEnum, + const QString &pyOpName, const QString &cppOpName, + bool boolResult = false); + + /// Writes the function that registers the multiple inheritance information for the classes that need it. + void writeMultipleInheritanceInitializerFunction(QTextStream &s, const AbstractMetaClass *metaClass); + /// Writes the implementation of special cast functions, used when we need to cast a class with multiple inheritance. + void writeSpecialCastFunction(QTextStream &s, const AbstractMetaClass *metaClass); + + void writePrimitiveConverterInitialization(QTextStream &s, const CustomConversion *customConversion); + void writeEnumConverterInitialization(QTextStream &s, const TypeEntry *enumType); + void writeEnumConverterInitialization(QTextStream &s, const AbstractMetaEnum *metaEnum); + void writeContainerConverterInitialization(QTextStream &s, const AbstractMetaType &type); + void writeSmartPointerConverterInitialization(QTextStream &s, const AbstractMetaType &ype); + void writeExtendedConverterInitialization(QTextStream &s, const TypeEntry *externalType, const QVector<const AbstractMetaClass *>& conversions); + + void writeParentChildManagement(QTextStream &s, const AbstractMetaFunction *func, bool userHeuristicForReturn); + bool writeParentChildManagement(QTextStream &s, const AbstractMetaFunction *func, int argIndex, bool userHeuristicPolicy); + void writeReturnValueHeuristics(QTextStream &s, const AbstractMetaFunction *func); + void writeInitQtMetaTypeFunctionBody(QTextStream &s, const GeneratorContext &context) const; + + /** + * Returns the multiple inheritance initializer function for the given class. + * \param metaClass the class for whom the function name must be generated. + * \return name of the multiple inheritance information initializer function or + * an empty string if there is no multiple inheritance in its ancestry. + */ + QString multipleInheritanceInitializerFunctionName(const AbstractMetaClass *metaClass); + + /// Returns a list of all classes to which the given class could be cast. + QStringList getAncestorMultipleInheritance(const AbstractMetaClass *metaClass); + + /// Returns true if the given class supports the python number protocol + bool supportsNumberProtocol(const AbstractMetaClass *metaClass); + + /// Returns true if the given class supports the python sequence protocol + bool supportsSequenceProtocol(const AbstractMetaClass *metaClass); + + /// Returns true if the given class supports the python mapping protocol + bool supportsMappingProtocol(const AbstractMetaClass *metaClass); + + /// Returns true if generator should produce getters and setters for the given class. + bool shouldGenerateGetSetList(const AbstractMetaClass *metaClass); + + void writeHashFunction(QTextStream &s, const GeneratorContext &context); + + /// Write default implementations for sequence protocol + void writeDefaultSequenceMethods(QTextStream &s, const GeneratorContext &context); + /// Helper function for writeStdListWrapperMethods. + void writeIndexError(QTextStream &s, const QString &errorMsg); + + QString writeReprFunction(QTextStream &s, const GeneratorContext &context, uint indirections); + + const AbstractMetaFunction *boolCast(const AbstractMetaClass *metaClass) const; + bool hasBoolCast(const AbstractMetaClass *metaClass) const + { return boolCast(metaClass) != nullptr; } + + AbstractMetaType findSmartPointerInstantiation(const TypeEntry *entry) const; + + // Number protocol structure members names. + static QHash<QString, QString> m_nbFuncs; + + // Maps special function names to function parameters and return types + // used by CPython API in the sequence protocol. + QHash<QString, QPair<QString, QString> > m_sequenceProtocol; + // Sequence protocol structure members names. + static QHash<QString, QString> m_sqFuncs; + + // Maps special function names to function parameters and return types + // used by CPython API in the mapping protocol. + QHash<QString, QPair<QString, QString> > m_mappingProtocol; + // Mapping protocol structure members names. + static QHash<QString, QString> m_mpFuncs; + + static QString m_currentErrorCode; + + /// Helper class to set and restore the current error code. + class ErrorCode { + public: + explicit ErrorCode(QString errorCode) { + m_savedErrorCode = CppGenerator::m_currentErrorCode; + CppGenerator::m_currentErrorCode = errorCode; + } + explicit ErrorCode(int errorCode) { + m_savedErrorCode = CppGenerator::m_currentErrorCode; + CppGenerator::m_currentErrorCode = QString::number(errorCode); + } + ~ErrorCode() { + CppGenerator::m_currentErrorCode = m_savedErrorCode; + } + private: + QString m_savedErrorCode; + }; +}; + +#endif // CPPGENERATOR_H diff --git a/sources/shiboken6/generator/shiboken/ctypenames.h b/sources/shiboken6/generator/shiboken/ctypenames.h new file mode 100644 index 000000000..abac261d5 --- /dev/null +++ b/sources/shiboken6/generator/shiboken/ctypenames.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CTYPENAMES_H +#define CTYPENAMES_H + +#include <QtCore/QString> + +static inline QString boolT() { return QStringLiteral("bool"); } +static inline QString intT() { return QStringLiteral("int"); } +static inline QString unsignedT() { return QStringLiteral("unsigned"); } +static inline QString unsignedIntT() { return QStringLiteral("unsigned int"); } +static inline QString longT() { return QStringLiteral("long"); } +static inline QString unsignedLongT() { return QStringLiteral("unsigned long"); } +static inline QString shortT() { return QStringLiteral("short"); } +static inline QString unsignedShortT() { return QStringLiteral("unsigned short"); } +static inline QString unsignedCharT() { return QStringLiteral("unsigned char"); } +static inline QString longLongT() { return QStringLiteral("long long"); } +static inline QString unsignedLongLongT() { return QStringLiteral("unsigned long long"); } +static inline QString charT() { return QStringLiteral("char"); } +static inline QString floatT() { return QStringLiteral("float"); } +static inline QString doubleT() { return QStringLiteral("double"); } +static inline QString constCharPtrT() { return QStringLiteral("const char*"); } + +static inline QString qByteArrayT() { return QStringLiteral("QByteArray"); } +static inline QString qMetaObjectT() { return QStringLiteral("QMetaObject"); } +static inline QString qObjectT() { return QStringLiteral("QObject"); } +static inline QString qStringT() { return QStringLiteral("QString"); } +static inline QString qVariantT() { return QStringLiteral("QVariant"); } + +#endif // CTYPENAMES_H diff --git a/sources/shiboken6/generator/shiboken/headergenerator.cpp b/sources/shiboken6/generator/shiboken/headergenerator.cpp new file mode 100644 index 000000000..d607ed643 --- /dev/null +++ b/sources/shiboken6/generator/shiboken/headergenerator.cpp @@ -0,0 +1,661 @@ +/**************************************************************************** +** +** 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 "headergenerator.h" +#include <abstractmetalang.h> +#include <typedatabase.h> +#include <reporthandler.h> +#include <fileout.h> +#include "parser/codemodel.h" + +#include <algorithm> + +#include <QtCore/QDir> +#include <QtCore/QTextStream> +#include <QtCore/QVariant> +#include <QtCore/QDebug> + +QString HeaderGenerator::fileNameSuffix() const +{ + return QLatin1String("_wrapper.h"); +} + +QString HeaderGenerator::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(); + } + QString fileNameBase = getFileNameBaseForSmartPointer(context.preciseType(), metaClass); + return fileNameBase + fileNameSuffix(); +} + +void HeaderGenerator::writeCopyCtor(QTextStream &s, const AbstractMetaClass *metaClass) const +{ + s << INDENT << wrapperName(metaClass) << "(const " << metaClass->qualifiedCppName() << "& self)"; + s << " : " << metaClass->qualifiedCppName() << "(self)\n"; + s << INDENT << "{\n"; + s << INDENT << "}\n\n"; +} + +void HeaderGenerator::writeProtectedFieldAccessors(QTextStream &s, const AbstractMetaField *field) const +{ + const AbstractMetaType &metaType = field->type(); + QString fieldType = metaType.cppSignature(); + QString fieldName = field->enclosingClass()->qualifiedCppName() + QLatin1String("::") + field->name(); + + // Force use of pointer to return internal variable memory + bool useReference = (!metaType.isConstant() && + !metaType.isEnum() && + !metaType.isPrimitive() && + metaType.indirections() == 0); + + + // Get function + s << INDENT << "inline " << fieldType + << (useReference ? " *" : " ") + << ' ' << protectedFieldGetterName(field) << "()" + << " { return " + << (useReference ? " &" : " ") << "this->" << fieldName << "; }\n"; + + // Set function + s << INDENT << "inline void " << protectedFieldSetterName(field) << '(' << fieldType << " value)" + << " { " << fieldName << " = value; }\n"; +} + +void HeaderGenerator::generateClass(QTextStream &s, const GeneratorContext &classContextIn) +{ + GeneratorContext classContext = classContextIn; + const AbstractMetaClass *metaClass = classContext.metaClass(); + m_inheritedOverloads.clear(); + Indentation indent(INDENT); + + // write license comment + s << licenseComment(); + + QString wrapperName; + if (!classContext.forSmartPointer()) { + wrapperName = classContext.useWrapper() + ? classContext.wrapperName() : metaClass->qualifiedCppName(); + } else { + wrapperName = classContext.smartPointerWrapperName(); + } + QString outerHeaderGuard = getFilteredCppSignatureString(wrapperName).toUpper(); + QString innerHeaderGuard; + + // Header + s << "#ifndef SBK_" << outerHeaderGuard << "_H\n"; + s << "#define SBK_" << outerHeaderGuard << "_H\n\n"; + + if (!avoidProtectedHack()) + s << "#define protected public\n\n"; + + //Includes + auto typeEntry = metaClass->typeEntry(); + s << typeEntry->include() << '\n'; + if (classContext.useWrapper() && !typeEntry->extraIncludes().isEmpty()) { + s << "\n// Extra includes\n"; + for (const Include &inc : typeEntry->extraIncludes()) + s << inc.toString() << '\n'; + } + + if (classContext.useWrapper() && usePySideExtensions() && metaClass->isQObject()) + s << "namespace PySide { class DynamicQMetaObject; }\n\n"; + + while (classContext.useWrapper()) { + if (!innerHeaderGuard.isEmpty()) { + s << "# ifndef SBK_" << innerHeaderGuard << "_H\n"; + s << "# define SBK_" << innerHeaderGuard << "_H\n\n"; + s << "// Inherited base class:\n"; + } + + // Class + s << "class " << wrapperName; + s << " : public " << metaClass->qualifiedCppName(); + + s << "\n{\npublic:\n"; + + const AbstractMetaFunctionList &funcs = filterFunctions(metaClass); + int maxOverrides = 0; + for (AbstractMetaFunction *func : funcs) { + if ((func->attributes() & AbstractMetaAttributes::FinalCppMethod) == 0) { + writeFunction(s, func); + // PYSIDE-803: Build a boolean cache for unused overrides. + if (shouldWriteVirtualMethodNative(func)) + maxOverrides++; + } + } + if (!maxOverrides) + maxOverrides = 1; + + if (avoidProtectedHack() && metaClass->hasProtectedFields()) { + const AbstractMetaFieldList &fields = metaClass->fields(); + for (AbstractMetaField *field : fields) { + if (!field->isProtected()) + continue; + writeProtectedFieldAccessors(s, field); + } + } + + //destructor + // PYSIDE-504: When C++ 11 is used, then the destructor must always be written. + // See generator.h for further reference. + if (!avoidProtectedHack() || !metaClass->hasPrivateDestructor() || alwaysGenerateDestructor) { + s << INDENT; + if (avoidProtectedHack() && metaClass->hasPrivateDestructor()) + s << "// C++11: need to declare (unimplemented) destructor because " + "the base class destructor is private.\n"; + s << '~' << wrapperName << "();\n"; + } + + writeClassCodeSnips(s, metaClass->typeEntry()->codeSnips(), + TypeSystem::CodeSnipPositionDeclaration, TypeSystem::NativeCode, + classContext); + + if ((!avoidProtectedHack() || !metaClass->hasPrivateDestructor()) + && usePySideExtensions() && metaClass->isQObject()) { + s << "public:\n"; + s << INDENT << "int qt_metacall(QMetaObject::Call call, int id, void **args) override;\n"; + s << INDENT << "void *qt_metacast(const char *_clname) override;\n"; + } + + if (!m_inheritedOverloads.isEmpty()) { + s << INDENT << "// Inherited overloads, because the using keyword sux\n"; + writeInheritedOverloads(s); + m_inheritedOverloads.clear(); + } + + if (usePySideExtensions()) + s << INDENT << "static void pysideInitQtMetaTypes();\n"; + + s << INDENT << "void resetPyMethodCache();\n"; + s << "private:\n"; + s << INDENT << "mutable bool m_PyMethodCache[" << maxOverrides << "];\n"; + + s << "};\n\n"; + if (!innerHeaderGuard.isEmpty()) + s << "# endif // SBK_" << innerHeaderGuard << "_H\n\n"; + + // PYSIDE-500: Use also includes for inherited wrapper classes, because + // without the protected hack, we sometimes need to cast inherited wrappers. + // But we don't use multiple include files. Instead, they are inserted as recursive + // headers. This keeps the file structure as simple as before the enhanced inheritance. + metaClass = metaClass->baseClass(); + if (!metaClass || !avoidProtectedHack()) + break; + classContext = contextForClass(metaClass); + if (!classContext.forSmartPointer()) { + wrapperName = classContext.useWrapper() + ? classContext.wrapperName() : metaClass->qualifiedCppName(); + } else { + wrapperName = classContext.smartPointerWrapperName(); + } + innerHeaderGuard = getFilteredCppSignatureString(wrapperName).toUpper(); + } + + s << "#endif // SBK_" << outerHeaderGuard << "_H\n\n"; +} + +void HeaderGenerator::writeFunction(QTextStream &s, const AbstractMetaFunction *func) +{ + + // do not write copy ctors here. + if (!func->isPrivate() && func->functionType() == AbstractMetaFunction::CopyConstructorFunction) { + writeCopyCtor(s, func->ownerClass()); + return; + } + if (func->isUserAdded()) + return; + + if (avoidProtectedHack() && func->isProtected() && !func->isConstructor() && !func->isOperatorOverload()) { + s << INDENT << "inline " << (func->isStatic() ? "static " : ""); + s << functionSignature(func, QString(), QLatin1String("_protected"), Generator::EnumAsInts|Generator::OriginalTypeDescription) + << " { "; + if (!func->isVoid()) + s << "return "; + if (!func->isAbstract()) + s << func->ownerClass()->qualifiedCppName() << "::"; + s << func->originalName() << '('; + QStringList args; + const AbstractMetaArgumentList &arguments = func->arguments(); + for (const AbstractMetaArgument &arg : arguments) { + QString argName = arg.name(); + const TypeEntry *enumTypeEntry = nullptr; + if (arg.type().isFlags()) + enumTypeEntry = static_cast<const FlagsTypeEntry *>(arg.type().typeEntry())->originator(); + else if (arg.type().isEnum()) + enumTypeEntry = arg.type().typeEntry(); + if (enumTypeEntry) + argName = QString::fromLatin1("%1(%2)").arg(arg.type().cppSignature(), argName); + args << argName; + } + s << args.join(QLatin1String(", ")) << ')'; + s << "; }\n"; + } + + // pure virtual functions need a default implementation + const bool notAbstract = !func->isAbstract(); + if ((func->isPrivate() && notAbstract && !visibilityModifiedToPrivate(func)) + || (func->isModifiedRemoved() && notAbstract)) + return; + + if (avoidProtectedHack() && func->ownerClass()->hasPrivateDestructor() + && (func->isAbstract() || func->isVirtual())) + return; + + if (func->isConstructor() || func->isAbstract() || func->isVirtual()) { + s << INDENT; + Options virtualOption = Generator::OriginalTypeDescription; + + const bool virtualFunc = func->isVirtual() || func->isAbstract(); + if (!virtualFunc && !func->hasSignatureModifications()) + virtualOption = Generator::NoOption; + + s << functionSignature(func, QString(), QString(), virtualOption); + + if (virtualFunc) + s << " override"; + s << ";\n"; + // Check if this method hide other methods in base classes + const AbstractMetaFunctionList &ownerFuncs = func->ownerClass()->functions(); + for (const AbstractMetaFunction *f : ownerFuncs) { + if (f != func + && !f->isConstructor() + && !f->isPrivate() + && !f->isVirtual() + && !f->isAbstract() + && !f->isStatic() + && f->name() == func->name()) { + m_inheritedOverloads << f; + } + } + + // TODO: when modified an abstract method ceases to be virtual but stays abstract + //if (func->isModifiedRemoved() && func->isAbstract()) { + //} + } +} + +static void _writeTypeIndexValue(QTextStream &s, const QString &variableName, + int typeIndex) +{ + s << " "; + s.setFieldWidth(56); + s << variableName; + s.setFieldWidth(0); + s << " = " << typeIndex; +} + +static inline void _writeTypeIndexValueLine(QTextStream &s, + const QString &variableName, + int typeIndex) +{ + _writeTypeIndexValue(s, variableName, typeIndex); + s << ",\n"; +} + +void HeaderGenerator::writeTypeIndexValueLine(QTextStream &s, const TypeEntry *typeEntry) +{ + if (!typeEntry || !typeEntry->generateCode()) + return; + s.setFieldAlignment(QTextStream::AlignLeft); + const int typeIndex = typeEntry->sbkIndex(); + _writeTypeIndexValueLine(s, getTypeIndexVariableName(typeEntry), typeIndex); + if (typeEntry->isComplex()) { + const auto *cType = static_cast<const ComplexTypeEntry *>(typeEntry); + if (cType->baseContainerType()) { + const AbstractMetaClass *metaClass = AbstractMetaClass::findClass(classes(), cType); + if (metaClass->templateBaseClass()) + _writeTypeIndexValueLine(s, getTypeIndexVariableName(metaClass, true), typeIndex); + } + } + if (typeEntry->isEnum()) { + auto ete = static_cast<const EnumTypeEntry *>(typeEntry); + if (ete->flags()) + writeTypeIndexValueLine(s, ete->flags()); + } +} + +void HeaderGenerator::writeTypeIndexValueLines(QTextStream &s, const AbstractMetaClass *metaClass) +{ + auto typeEntry = metaClass->typeEntry(); + if (!typeEntry->generateCode()) + return; + // enum indices are required for invisible namespaces as well. + for (const AbstractMetaEnum *metaEnum : metaClass->enums()) { + if (!metaEnum->isPrivate()) + writeTypeIndexValueLine(s, metaEnum->typeEntry()); + } + if (NamespaceTypeEntry::isVisibleScope(typeEntry)) + writeTypeIndexValueLine(s, metaClass->typeEntry()); +} + +// Format the typedefs for the typedef entries to be generated +static void formatTypeDefEntries(QTextStream &s) +{ + QVector<const TypedefEntry *> entries; + const auto typeDbEntries = TypeDatabase::instance()->typedefEntries(); + for (auto it = typeDbEntries.cbegin(), end = typeDbEntries.cend(); it != end; ++it) { + if (it.value()->generateCode() != 0) + entries.append(it.value()); + } + if (entries.isEmpty()) + return; + s << "\n// typedef entries\n"; + for (const auto e : entries) { + const QString name = e->qualifiedCppName(); + // Fixme: simplify by using nested namespaces in C++ 17. + const auto components = QStringView{name}.split(u"::"); + const int nameSpaceCount = components.size() - 1; + for (int n = 0; n < nameSpaceCount; ++n) + s << "namespace " << components.at(n) << " {\n"; + s << "using " << components.constLast() << " = " << e->sourceType() << ";\n"; + for (int n = 0; n < nameSpaceCount; ++n) + s << "}\n"; + } + s << '\n'; +} + + +bool HeaderGenerator::finishGeneration() +{ + // Generate the main header for this module. + // This header should be included by binding modules + // extendind on top of this one. + QSet<Include> includes; + QString macros; + QTextStream macrosStream(¯os); + QString sbkTypeFunctions; + QTextStream typeFunctions(&sbkTypeFunctions); + QString protectedEnumSurrogates; + QTextStream protEnumsSurrogates(&protectedEnumSurrogates); + + const auto snips = TypeDatabase::instance()->defaultTypeSystemType()->codeSnips(); + if (!snips.isEmpty()) { + writeCodeSnips(macrosStream, snips, TypeSystem::CodeSnipPositionDeclaration, + TypeSystem::TargetLangCode); + } + + Indentation indent(INDENT); + + macrosStream << "// Type indices\nenum : int {\n"; + AbstractMetaClassList classList = classes(); + + std::sort(classList.begin(), classList.end(), [](AbstractMetaClass *a, AbstractMetaClass *b) { + return a->typeEntry()->sbkIndex() < b->typeEntry()->sbkIndex(); + }); + + for (const AbstractMetaClass *metaClass : classList) + writeTypeIndexValueLines(macrosStream, metaClass); + + for (const AbstractMetaEnum *metaEnum : globalEnums()) + writeTypeIndexValueLine(macrosStream, metaEnum->typeEntry()); + + // Write the smart pointer define indexes. + int smartPointerCountIndex = getMaxTypeIndex(); + int smartPointerCount = 0; + const QVector<AbstractMetaType> &instantiatedSmartPtrs = instantiatedSmartPointers(); + for (const AbstractMetaType &metaType : instantiatedSmartPtrs) { + QString indexName = getTypeIndexVariableName(metaType); + _writeTypeIndexValue(macrosStream, indexName, smartPointerCountIndex); + macrosStream << ", // " << metaType.cppSignature() << Qt::endl; + // Add a the same value for const pointees (shared_ptr<const Foo>). + const auto ptrName = metaType.typeEntry()->entryName(); + int pos = indexName.indexOf(ptrName, 0, Qt::CaseInsensitive); + if (pos >= 0) { + indexName.insert(pos + ptrName.size() + 1, QLatin1String("CONST")); + _writeTypeIndexValue(macrosStream, indexName, smartPointerCountIndex); + macrosStream << ", // (const)\n"; + } + ++smartPointerCountIndex; + ++smartPointerCount; + } + + _writeTypeIndexValue(macrosStream, + QLatin1String("SBK_") + moduleName() + QLatin1String("_IDX_COUNT"), + getMaxTypeIndex() + smartPointerCount); + macrosStream << "\n};\n"; + + macrosStream << "// This variable stores all Python types exported by this module.\n"; + macrosStream << "extern PyTypeObject **" << cppApiVariableName() << ";\n\n"; + macrosStream << "// This variable stores the Python module object exported by this module.\n"; + macrosStream << "extern PyObject *" << pythonModuleObjectName() << ";\n\n"; + macrosStream << "// This variable stores all type converters exported by this module.\n"; + macrosStream << "extern SbkConverter **" << convertersVariableName() << ";\n\n"; + + // TODO-CONVERTER ------------------------------------------------------------------------------ + // Using a counter would not do, a fix must be made to APIExtractor's getTypeIndex(). + macrosStream << "// Converter indices\nenum : int {\n"; + const PrimitiveTypeEntryList &primitives = primitiveTypes(); + int pCount = 0; + for (const PrimitiveTypeEntry *ptype : primitives) { + /* Note: do not generate indices for typedef'd primitive types + * as they'll use the primitive type converters instead, so we + * don't need to create any other. + */ + if (!ptype->generateCode() || !ptype->customConversion()) + continue; + + _writeTypeIndexValueLine(macrosStream, getTypeIndexVariableName(ptype), pCount++); + } + + const QVector<AbstractMetaType> &containers = instantiatedContainers(); + for (const AbstractMetaType &container : containers) { + _writeTypeIndexValue(macrosStream, getTypeIndexVariableName(container), pCount); + macrosStream << ", // " << container.cppSignature() << Qt::endl; + pCount++; + } + + // Because on win32 the compiler will not accept a zero length array. + if (pCount == 0) + pCount++; + _writeTypeIndexValue(macrosStream, QStringLiteral("SBK_%1_CONVERTERS_IDX_COUNT") + .arg(moduleName()), pCount); + macrosStream << "\n};\n"; + + formatTypeDefEntries(macrosStream); + + // TODO-CONVERTER ------------------------------------------------------------------------------ + + macrosStream << "// Macros for type check\n"; + + if (usePySideExtensions()) { + typeFunctions << "QT_WARNING_PUSH\n"; + typeFunctions << "QT_WARNING_DISABLE_DEPRECATED\n"; + } + for (const AbstractMetaEnum *cppEnum : globalEnums()) { + if (!cppEnum->isAnonymous()) { + includes << cppEnum->typeEntry()->include(); + writeSbkTypeFunction(typeFunctions, cppEnum); + } + } + + for (AbstractMetaClass *metaClass : classList) { + if (!shouldGenerate(metaClass)) + continue; + + //Includes + const TypeEntry *classType = metaClass->typeEntry(); + includes << classType->include(); + + for (const AbstractMetaEnum *cppEnum : metaClass->enums()) { + if (cppEnum->isAnonymous() || cppEnum->isPrivate()) + continue; + EnumTypeEntry *enumType = cppEnum->typeEntry(); + includes << enumType->include(); + writeProtectedEnumSurrogate(protEnumsSurrogates, cppEnum); + writeSbkTypeFunction(typeFunctions, cppEnum); + } + + if (!metaClass->isNamespace()) + writeSbkTypeFunction(typeFunctions, metaClass); + } + + for (const AbstractMetaType &metaType : instantiatedSmartPtrs) { + const TypeEntry *classType = metaType.typeEntry(); + includes << classType->include(); + writeSbkTypeFunction(typeFunctions, metaType); + } + if (usePySideExtensions()) + typeFunctions << "QT_WARNING_POP\n"; + + QString moduleHeaderFileName(outputDirectory() + + QDir::separator() + subDirectoryForPackage(packageName()) + + QDir::separator() + getModuleHeaderFileName()); + + QString includeShield(QLatin1String("SBK_") + moduleName().toUpper() + QLatin1String("_PYTHON_H")); + + FileOut file(moduleHeaderFileName); + QTextStream &s = file.stream; + // write license comment + s << licenseComment() << Qt::endl << Qt::endl; + + s << "#ifndef " << includeShield << Qt::endl; + s << "#define " << includeShield << Qt::endl << Qt::endl; + if (!avoidProtectedHack()) { + s << "//workaround to access protected functions\n"; + s << "#define protected public\n\n"; + } + + s << "#include <sbkpython.h>\n"; + s << "#include <sbkconverter.h>\n"; + + QStringList requiredTargetImports = TypeDatabase::instance()->requiredTargetImports(); + if (!requiredTargetImports.isEmpty()) { + s << "// Module Includes\n"; + for (const QString &requiredModule : qAsConst(requiredTargetImports)) + s << "#include <" << getModuleHeaderFileName(requiredModule) << ">\n"; + s << Qt::endl; + } + + s << "// Bound library includes\n"; + for (const Include &include : qAsConst(includes)) + s << include; + + if (!primitiveTypes().isEmpty()) { + s << "// Conversion Includes - Primitive Types\n"; + const PrimitiveTypeEntryList &primitiveTypeList = primitiveTypes(); + for (const PrimitiveTypeEntry *ptype : primitiveTypeList) + s << ptype->include(); + s << Qt::endl; + } + + if (!containerTypes().isEmpty()) { + s << "// Conversion Includes - Container Types\n"; + const ContainerTypeEntryList &containerTypeList = containerTypes(); + for (const ContainerTypeEntry *ctype : containerTypeList) + s << ctype->include(); + s << Qt::endl; + } + + s << macros << Qt::endl; + + if (!protectedEnumSurrogates.isEmpty()) { + s << "// Protected enum surrogates\n"; + s << protectedEnumSurrogates << Qt::endl; + } + + s << "namespace Shiboken\n{\n\n"; + + s << "// PyType functions, to get the PyObjectType for a type T\n"; + s << sbkTypeFunctions << Qt::endl; + + s << "} // namespace Shiboken\n\n"; + + s << "#endif // " << includeShield << Qt::endl << Qt::endl; + + return file.done() != FileOut::Failure; +} + +void HeaderGenerator::writeProtectedEnumSurrogate(QTextStream &s, const AbstractMetaEnum *cppEnum) +{ + if (avoidProtectedHack() && cppEnum->isProtected()) + s << "enum " << protectedEnumSurrogateName(cppEnum) << " {};\n"; +} + +void HeaderGenerator::writeSbkTypeFunction(QTextStream &s, const AbstractMetaEnum *cppEnum) +{ + QString enumName; + if (avoidProtectedHack() && cppEnum->isProtected()) { + enumName = protectedEnumSurrogateName(cppEnum); + } else { + enumName = cppEnum->name(); + if (cppEnum->enclosingClass()) + enumName = cppEnum->enclosingClass()->qualifiedCppName() + QLatin1String("::") + enumName; + } + + s << "template<> inline PyTypeObject *SbkType< ::" << enumName << " >() "; + s << "{ return " << cpythonTypeNameExt(cppEnum->typeEntry()) << "; }\n"; + + FlagsTypeEntry *flag = cppEnum->typeEntry()->flags(); + if (flag) { + s << "template<> inline PyTypeObject *SbkType< ::" << flag->name() << " >() " + << "{ return " << cpythonTypeNameExt(flag) << "; }\n"; + } +} + +void HeaderGenerator::writeSbkTypeFunction(QTextStream &s, const AbstractMetaClass *cppClass) +{ + s << "template<> inline PyTypeObject *SbkType< ::" << cppClass->qualifiedCppName() << " >() " + << "{ return reinterpret_cast<PyTypeObject *>(" << cpythonTypeNameExt(cppClass->typeEntry()) << "); }\n"; +} + +void HeaderGenerator::writeSbkTypeFunction(QTextStream &s, const AbstractMetaType &metaType) +{ + s << "template<> inline PyTypeObject *SbkType< ::" << metaType.cppSignature() << " >() " + << "{ return reinterpret_cast<PyTypeObject *>(" << cpythonTypeNameExt(metaType) << "); }\n"; +} + +void HeaderGenerator::writeInheritedOverloads(QTextStream &s) +{ + for (const AbstractMetaFunction *func : qAsConst(m_inheritedOverloads)) { + s << INDENT << "inline "; + s << functionSignature(func, QString(), QString(), Generator::EnumAsInts|Generator::OriginalTypeDescription) + << " { "; + if (!func->isVoid()) + s << "return "; + s << func->ownerClass()->qualifiedCppName() << "::" << func->originalName() << '('; + QStringList args; + const AbstractMetaArgumentList &arguments = func->arguments(); + for (const AbstractMetaArgument &arg : arguments) { + QString argName = arg.name(); + const TypeEntry *enumTypeEntry = nullptr; + if (arg.type().isFlags()) + enumTypeEntry = static_cast<const FlagsTypeEntry *>(arg.type().typeEntry())->originator(); + else if (arg.type().isEnum()) + enumTypeEntry = arg.type().typeEntry(); + if (enumTypeEntry) + argName = arg.type().cppSignature() + QLatin1Char('(') + argName + QLatin1Char(')'); + args << argName; + } + s << args.join(QLatin1String(", ")) << ')'; + s << "; }\n"; + } +} diff --git a/sources/shiboken6/generator/shiboken/headergenerator.h b/sources/shiboken6/generator/shiboken/headergenerator.h new file mode 100644 index 000000000..d02516b8d --- /dev/null +++ b/sources/shiboken6/generator/shiboken/headergenerator.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef HEADERGENERATOR_H +#define HEADERGENERATOR_H + +#include "shibokengenerator.h" + +#include <QtCore/QSet> + +class AbstractMetaFunction; + +/** + * The HeaderGenerator generate the declarations of C++ bindings classes. + */ +class HeaderGenerator : public ShibokenGenerator +{ +public: + OptionDescriptions options() const override { return OptionDescriptions(); } + + const char *name() const override { return "Header generator"; } + +protected: + QString fileNameSuffix() const override; + QString fileNameForContext(const GeneratorContext &context) const override; + void generateClass(QTextStream &s, const GeneratorContext &classContext) override; + bool finishGeneration() override; + +private: + void writeCopyCtor(QTextStream &s, const AbstractMetaClass *metaClass) const; + void writeProtectedFieldAccessors(QTextStream &s, const AbstractMetaField *field) const; + void writeFunction(QTextStream &s, const AbstractMetaFunction *func); + void writeSbkTypeFunction(QTextStream &s, const AbstractMetaEnum *cppEnum); + void writeSbkTypeFunction(QTextStream &s, const AbstractMetaClass *cppClass); + void writeSbkTypeFunction(QTextStream &s, const AbstractMetaType &metaType); + void writeTypeIndexValueLine(QTextStream &s, const TypeEntry *typeEntry); + void writeTypeIndexValueLines(QTextStream &s, const AbstractMetaClass *metaClass); + void writeProtectedEnumSurrogate(QTextStream &s, const AbstractMetaEnum *cppEnum); + void writeInheritedOverloads(QTextStream &s); + + QSet<const AbstractMetaFunction *> m_inheritedOverloads; +}; + +#endif // HEADERGENERATOR_H + diff --git a/sources/shiboken6/generator/shiboken/overloaddata.cpp b/sources/shiboken6/generator/shiboken/overloaddata.cpp new file mode 100644 index 000000000..53fff9f25 --- /dev/null +++ b/sources/shiboken6/generator/shiboken/overloaddata.cpp @@ -0,0 +1,1093 @@ +/**************************************************************************** +** +** 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 <abstractmetalang.h> +#include <reporthandler.h> +#include <graph.h> +#include "overloaddata.h" +#include "ctypenames.h" +#include "indentor.h" +#include "shibokengenerator.h" + +#include <QtCore/QDir> +#include <QtCore/QFile> +#include <QtCore/QTemporaryFile> + +#include <algorithm> + +static const TypeEntry *getReferencedTypeEntry(const TypeEntry *typeEntry) +{ + if (typeEntry->isPrimitive()) { + auto pte = dynamic_cast<const PrimitiveTypeEntry *>(typeEntry); + while (pte->referencedTypeEntry()) + pte = pte->referencedTypeEntry(); + typeEntry = pte; + } + return typeEntry; +} + +static QString getTypeName(const AbstractMetaType &type) +{ + const TypeEntry *typeEntry = getReferencedTypeEntry(type.typeEntry()); + QString typeName = typeEntry->name(); + if (typeEntry->isContainer()) { + QStringList types; + for (const auto &cType : type.instantiations()) { + const TypeEntry *typeEntry = getReferencedTypeEntry(cType.typeEntry()); + types << typeEntry->name(); + } + typeName += QLatin1Char('<') + types.join(QLatin1Char(',')) + QLatin1String(" >"); + } + return typeName; +} + +static QString getTypeName(const OverloadData *ov) +{ + return ov->hasArgumentTypeReplace() ? ov->argumentTypeReplaced() : getTypeName(ov->argType()); +} + +static bool typesAreEqual(const AbstractMetaType &typeA, const AbstractMetaType &typeB) +{ + if (typeA.typeEntry() == typeB.typeEntry()) { + if (typeA.isContainer() || typeA.isSmartPointer()) { + if (typeA.instantiations().size() != typeB.instantiations().size()) + return false; + + for (int i = 0; i < typeA.instantiations().size(); ++i) { + if (!typesAreEqual(typeA.instantiations().at(i), typeB.instantiations().at(i))) + return false; + } + return true; + } + + return !(ShibokenGenerator::isCString(typeA) ^ ShibokenGenerator::isCString(typeB)); + } + return false; +} + + +/** + * OverloadSortData just helps writing clearer code in the + * OverloadData::sortNextOverloads method. + */ +struct OverloadSortData +{ + /** + * Adds a typeName into the type map without associating it with + * a OverloadData. This is done to express type dependencies that could + * or could not appear in overloaded signatures not processed yet. + */ + void mapType(const QString &typeName) + { + if (map.contains(typeName)) + return; + map[typeName] = counter; + if (!reverseMap.contains(counter)) + reverseMap[counter] = nullptr; + counter++; + } + + void mapType(OverloadData *overloadData) + { + QString typeName = getTypeName(overloadData); + map[typeName] = counter; + reverseMap[counter] = overloadData; + counter++; + } + + int lastProcessedItemId() { return counter - 1; } + + int counter = 0; + QHash<QString, int> map; // typeName -> id + QHash<int, OverloadData *> reverseMap; // id -> OverloadData; +}; + +/** + * Helper function that returns the name of a container get from containerType argument and + * an instantiation taken either from an implicit conversion expressed by the function argument, + * or from the string argument implicitConvTypeName. + */ +static QString getImplicitConversionTypeName(const AbstractMetaType &containerType, + const AbstractMetaType &instantiation, + const AbstractMetaFunction *function, + const QString &implicitConvTypeName = QString()) +{ + QString impConv; + if (!implicitConvTypeName.isEmpty()) + impConv = implicitConvTypeName; + else if (function->isConversionOperator()) + impConv = function->ownerClass()->typeEntry()->name(); + else + impConv = getTypeName(function->arguments().constFirst().type()); + + QStringList types; + for (const auto &otherType : containerType.instantiations()) + types << (otherType == instantiation ? impConv : getTypeName(otherType)); + + return containerType.typeEntry()->qualifiedCppName() + QLatin1Char('<') + + types.join(QLatin1String(", ")) + QLatin1String(" >"); +} + +// overloaddata.cpp +static QString msgCyclicDependency(const QString &funcName, const QString &graphName, + const AbstractMetaFunctionCList &cyclic, + const OverloadData::MetaFunctionList &involvedConversions) +{ + QString result; + QTextStream str(&result); + str << "Cyclic dependency found on overloaddata for \"" << funcName + << "\" method! The graph boy saved the graph at \"" << QDir::toNativeSeparators(graphName) + << "\". Cyclic functions:"; + for (auto c : cyclic) + str << ' ' << c->signature(); + if (const int count = involvedConversions.size()) { + str << " Implicit conversions (" << count << "): "; + for (int i = 0; i < count; ++i) { + if (i) + str << ", \""; + str << involvedConversions.at(i)->signature() << '"'; + if (const AbstractMetaClass *c = involvedConversions.at(i)->implementingClass()) + str << '(' << c->name() << ')'; + } + } + return result; +} + +static inline int overloadNumber(const OverloadData *o) +{ + return o->referenceFunction()->overloadNumber(); +} + +bool OverloadData::sortByOverloadNumberModification() +{ + if (std::all_of(m_nextOverloadData.cbegin(), m_nextOverloadData.cend(), + [](const OverloadData *o) { return overloadNumber(o) == TypeSystem::OverloadNumberDefault; })) { + return false; + } + std::stable_sort(m_nextOverloadData.begin(), m_nextOverloadData.end(), + [] (const OverloadData *o1, const OverloadData *o2) { + return overloadNumber(o1) < overloadNumber(o2); + }); + return true; +} + +/** + * Topologically sort the overloads by implicit convertion order + * + * This avoids using an implicit conversion if there's an explicit + * overload for the convertible type. So, if there's an implicit convert + * like TargetType(ConvertibleType foo) and both are in the overload list, + * ConvertibleType is checked before TargetType. + * + * Side effects: Modifies m_nextOverloadData + */ +void OverloadData::sortNextOverloads() +{ + OverloadSortData sortData; + bool checkPyObject = false; + int pyobjectIndex = 0; + bool checkPySequence = false; + int pySeqIndex = 0; + bool checkQString = false; + int qstringIndex = 0; + bool checkQVariant = false; + int qvariantIndex = 0; + bool checkPyBuffer = false; + int pyBufferIndex = 0; + + // Primitive types that are not int, long, short, + // char and their respective unsigned counterparts. + static const QStringList nonIntegerPrimitives{floatT(), doubleT(), boolT()}; + + // Signed integer primitive types. + static const QStringList signedIntegerPrimitives{intT(), shortT(), longT(), longLongT()}; + + // sort the children overloads + for (OverloadData *ov : qAsConst(m_nextOverloadData)) + ov->sortNextOverloads(); + + if (m_nextOverloadData.size() <= 1 || sortByOverloadNumberModification()) + return; + + // Populates the OverloadSortData object containing map and reverseMap, to map type names to ids, + // these ids will be used by the topological sort algorithm, because is easier and faster to work + // with graph sorting using integers. + for (OverloadData *ov : qAsConst(m_nextOverloadData)) { + sortData.mapType(ov); + + const QString typeName(getTypeName(ov)); + + if (!checkPyObject && typeName.contains(QLatin1String("PyObject"))) { + checkPyObject = true; + pyobjectIndex = sortData.lastProcessedItemId(); + } else if (!checkPySequence && typeName == QLatin1String("PySequence")) { + checkPySequence = true; + pySeqIndex = sortData.lastProcessedItemId(); + } else if (!checkPyBuffer && typeName == QLatin1String("PyBuffer")) { + checkPyBuffer = true; + pyBufferIndex = sortData.lastProcessedItemId(); + } else if (!checkQVariant && typeName == qVariantT()) { + checkQVariant = true; + qvariantIndex = sortData.lastProcessedItemId(); + } else if (!checkQString && typeName == qStringT()) { + checkQString = true; + qstringIndex = sortData.lastProcessedItemId(); + } + + for (const auto &instantiation : ov->argType().instantiations()) { + // Add dependencies for type instantiation of container. + QString typeName = getTypeName(instantiation); + sortData.mapType(typeName); + + // Build dependency for implicit conversion types instantiations for base container. + // For example, considering signatures "method(list<PointF>)" and "method(list<Point>)", + // and being PointF implicitly convertible from Point, an list<T> instantiation with T + // as Point must come before the PointF instantiation, or else list<Point> will never + // be called. In the case of primitive types, list<double> must come before list<int>. + if (instantiation.isPrimitive() && (signedIntegerPrimitives.contains(instantiation.name()))) { + for (const QString &primitive : qAsConst(nonIntegerPrimitives)) + sortData.mapType(getImplicitConversionTypeName(ov->argType(), instantiation, nullptr, primitive)); + } else { + const AbstractMetaFunctionList &funcs = m_generator->implicitConversions(instantiation); + for (const AbstractMetaFunction *function : funcs) + sortData.mapType(getImplicitConversionTypeName(ov->argType(), instantiation, function)); + } + } + } + + + // Create the graph of type dependencies based on implicit conversions. + Graph graph(sortData.reverseMap.count()); + // All C++ primitive types, add any forgotten type AT THE END OF THIS LIST! + static const QStringList primitiveTypes{intT(), unsignedIntT(), longT(), unsignedLongT(), + shortT(), unsignedShortT(), boolT(), unsignedCharT(), charT(), floatT(), + doubleT(), constCharPtrT()}; + + QList<int> foundPrimitiveTypeIds; + for (const auto &p : primitiveTypes) { + const auto it = sortData.map.constFind(p); + if (it != sortData.map.cend()) + foundPrimitiveTypeIds.append(it.value()); + } + + if (checkPySequence && checkPyObject) + graph.addEdge(pySeqIndex, pyobjectIndex); + + QStringList classesWithIntegerImplicitConversion; + + MetaFunctionList involvedConversions; + + for (OverloadData *ov : qAsConst(m_nextOverloadData)) { + const AbstractMetaType &targetType = ov->argType(); + const QString targetTypeEntryName(getTypeName(ov)); + int targetTypeId = sortData.map[targetTypeEntryName]; + + // Process implicit conversions + const AbstractMetaFunctionList &functions = m_generator->implicitConversions(targetType); + for (AbstractMetaFunction *function : functions) { + QString convertibleType; + if (function->isConversionOperator()) + convertibleType = function->ownerClass()->typeEntry()->name(); + else + convertibleType = getTypeName(function->arguments().constFirst().type()); + + if (convertibleType == intT() || convertibleType == unsignedIntT()) + classesWithIntegerImplicitConversion << targetTypeEntryName; + + if (!sortData.map.contains(convertibleType)) + continue; + + int convertibleTypeId = sortData.map[convertibleType]; + + // If a reverse pair already exists, remove it. Probably due to the + // container check (This happened to QVariant and QHash) + graph.removeEdge(targetTypeId, convertibleTypeId); + graph.addEdge(convertibleTypeId, targetTypeId); + involvedConversions.append(function); + } + + // Process inheritance relationships + if (targetType.isValue() || targetType.isObject()) { + const AbstractMetaClass *metaClass = AbstractMetaClass::findClass(m_generator->classes(), targetType.typeEntry()); + const AbstractMetaClassList &ancestors = m_generator->getAllAncestors(metaClass); + for (const AbstractMetaClass *ancestor : ancestors) { + QString ancestorTypeName = ancestor->typeEntry()->name(); + if (!sortData.map.contains(ancestorTypeName)) + continue; + int ancestorTypeId = sortData.map[ancestorTypeName]; + graph.removeEdge(ancestorTypeId, targetTypeId); + graph.addEdge(targetTypeId, ancestorTypeId); + } + } + + // Process template instantiations + for (const auto &instantiation : targetType.instantiations()) { + if (sortData.map.contains(getTypeName(instantiation))) { + int convertible = sortData.map[getTypeName(instantiation)]; + + if (!graph.containsEdge(targetTypeId, convertible)) // Avoid cyclic dependency. + graph.addEdge(convertible, targetTypeId); + + if (instantiation.isPrimitive() && (signedIntegerPrimitives.contains(instantiation.name()))) { + for (const QString &primitive : qAsConst(nonIntegerPrimitives)) { + QString convertibleTypeName = getImplicitConversionTypeName(ov->argType(), instantiation, nullptr, primitive); + if (!graph.containsEdge(targetTypeId, sortData.map[convertibleTypeName])) // Avoid cyclic dependency. + graph.addEdge(sortData.map[convertibleTypeName], targetTypeId); + } + + } else { + const AbstractMetaFunctionList &funcs = m_generator->implicitConversions(instantiation); + for (const AbstractMetaFunction *function : funcs) { + QString convertibleTypeName = getImplicitConversionTypeName(ov->argType(), instantiation, function); + if (!graph.containsEdge(targetTypeId, sortData.map[convertibleTypeName])) { // Avoid cyclic dependency. + graph.addEdge(sortData.map[convertibleTypeName], targetTypeId); + involvedConversions.append(function); + } + } + } + } + } + + + if ((checkPySequence || checkPyObject || checkPyBuffer) + && !targetTypeEntryName.contains(QLatin1String("PyObject")) + && !targetTypeEntryName.contains(QLatin1String("PyBuffer")) + && !targetTypeEntryName.contains(QLatin1String("PySequence"))) { + if (checkPySequence) { + // PySequence will be checked after all more specific types, but before PyObject. + graph.addEdge(targetTypeId, pySeqIndex); + } else if (checkPyBuffer) { + // PySequence will be checked after all more specific types, but before PyObject. + graph.addEdge(targetTypeId, pyBufferIndex); + } else { + // Add dependency on PyObject, so its check is the last one (too generic). + graph.addEdge(targetTypeId, pyobjectIndex); + } + } else if (checkQVariant && targetTypeEntryName != qVariantT()) { + if (!graph.containsEdge(qvariantIndex, targetTypeId)) // Avoid cyclic dependency. + graph.addEdge(targetTypeId, qvariantIndex); + } else if (checkQString && ShibokenGenerator::isPointer(ov->argType()) + && targetTypeEntryName != qStringT() + && targetTypeEntryName != qByteArrayT() + && (!checkPyObject || targetTypeId != pyobjectIndex)) { + if (!graph.containsEdge(qstringIndex, targetTypeId)) // Avoid cyclic dependency. + graph.addEdge(targetTypeId, qstringIndex); + } + + if (targetType.isEnum()) { + // Enum values must precede primitive types. + for (auto id : foundPrimitiveTypeIds) + graph.addEdge(targetTypeId, id); + } + } + + // QByteArray args need to be checked after QString args + if (sortData.map.contains(qStringT()) && sortData.map.contains(qByteArrayT())) + graph.addEdge(sortData.map.value(qStringT()), sortData.map.value(qByteArrayT())); + + for (OverloadData *ov : qAsConst(m_nextOverloadData)) { + const AbstractMetaType &targetType = ov->argType(); + if (!targetType.isEnum()) + continue; + + QString targetTypeEntryName = getTypeName(targetType); + // Enum values must precede types implicitly convertible from "int" or "unsigned int". + for (const QString &implicitFromInt : qAsConst(classesWithIntegerImplicitConversion)) + graph.addEdge(sortData.map[targetTypeEntryName], sortData.map[implicitFromInt]); + } + + + // Special case for double(int i) (not tracked by m_generator->implicitConversions + for (const QString &signedIntegerName : qAsConst(signedIntegerPrimitives)) { + if (sortData.map.contains(signedIntegerName)) { + for (const QString &nonIntegerName : qAsConst(nonIntegerPrimitives)) { + if (sortData.map.contains(nonIntegerName)) + graph.addEdge(sortData.map[nonIntegerName], sortData.map[signedIntegerName]); + } + } + } + + // sort the overloads topologically based on the dependency graph. + const auto unmappedResult = graph.topologicalSort(); + if (!unmappedResult.isValid()) { + QString funcName = referenceFunction()->name(); + if (referenceFunction()->ownerClass()) + funcName.prepend(referenceFunction()->ownerClass()->name() + QLatin1Char('.')); + + // Dump overload graph + QString graphName = QDir::tempPath() + QLatin1Char('/') + funcName + QLatin1String(".dot"); + QHash<int, QString> nodeNames; + for (auto it = sortData.map.cbegin(), end = sortData.map.cend(); it != end; ++it) + nodeNames.insert(it.value(), it.key()); + graph.dumpDot(nodeNames, graphName); + AbstractMetaFunctionCList cyclic; + for (int c : unmappedResult.cyclic) + cyclic.append(sortData.reverseMap.value(c)->referenceFunction()); + qCWarning(lcShiboken, "%s", qPrintable(msgCyclicDependency(funcName, graphName, cyclic, involvedConversions))); + } + + m_nextOverloadData.clear(); + for (int i : unmappedResult.result) { + if (!sortData.reverseMap[i]) + continue; + m_nextOverloadData << sortData.reverseMap[i]; + } +} + +/** + * Root constructor for OverloadData + * + * This constructor receives the list of overloads for a given function and iterates generating + * the graph of OverloadData instances. Each OverloadData instance references an argument/type + * combination. + * + * Example: + * addStuff(double, PyObject *) + * addStuff(double, int) + * + * Given these two overloads, there will be the following graph: + * + * addStuff - double - PyObject * + * \- int + * + */ +OverloadData::OverloadData(const AbstractMetaFunctionList &overloads, const ShibokenGenerator *generator) + : m_minArgs(256), m_maxArgs(0), m_argPos(-1), m_argType(nullptr), + m_headOverloadData(this), m_previousOverloadData(nullptr), m_generator(generator) +{ + for (const AbstractMetaFunction *func : overloads) { + m_overloads.append(func); + int argSize = func->arguments().size() - numberOfRemovedArguments(func); + if (m_minArgs > argSize) + m_minArgs = argSize; + else if (m_maxArgs < argSize) + m_maxArgs = argSize; + OverloadData *currentOverloadData = this; + const AbstractMetaArgumentList &arguments = func->arguments(); + for (const AbstractMetaArgument &arg : arguments) { + if (func->argumentRemoved(arg.argumentIndex() + 1)) + continue; + currentOverloadData = currentOverloadData->addOverloadData(func, arg); + } + } + + // Sort the overload possibilities so that the overload decisor code goes for the most + // important cases first, based on the topological order of the implicit conversions + sortNextOverloads(); + + // Fix minArgs + if (minArgs() > maxArgs()) + m_headOverloadData->m_minArgs = maxArgs(); +} + +OverloadData::OverloadData(OverloadData *headOverloadData, const AbstractMetaFunction *func, + const AbstractMetaType &argType, int argPos) + : m_minArgs(256), m_maxArgs(0), m_argPos(argPos), m_argType(argType), + m_headOverloadData(headOverloadData), m_previousOverloadData(nullptr), + m_generator(nullptr) +{ + if (func) + this->addOverload(func); +} + +void OverloadData::addOverload(const AbstractMetaFunction *func) +{ + int origNumArgs = func->arguments().size(); + int removed = numberOfRemovedArguments(func); + int numArgs = origNumArgs - removed; + + if (numArgs > m_headOverloadData->m_maxArgs) + m_headOverloadData->m_maxArgs = numArgs; + + if (numArgs < m_headOverloadData->m_minArgs) + m_headOverloadData->m_minArgs = numArgs; + + for (int i = 0; m_headOverloadData->m_minArgs > 0 && i < origNumArgs; i++) { + if (func->argumentRemoved(i + 1)) + continue; + if (func->arguments().at(i).hasDefaultValueExpression()) { + int fixedArgIndex = i - removed; + if (fixedArgIndex < m_headOverloadData->m_minArgs) + m_headOverloadData->m_minArgs = fixedArgIndex; + } + } + + m_overloads.append(func); +} + +OverloadData *OverloadData::addOverloadData(const AbstractMetaFunction *func, + const AbstractMetaArgument &arg) +{ + const AbstractMetaType &argType = arg.type(); + OverloadData *overloadData = nullptr; + if (!func->isOperatorOverload()) { + for (OverloadData *tmp : qAsConst(m_nextOverloadData)) { + // TODO: 'const char *', 'char *' and 'char' will have the same TypeEntry? + + // If an argument have a type replacement, then we should create a new overloaddata + // for it, unless the next argument also have a identical type replacement. + QString replacedArg = func->typeReplaced(tmp->m_argPos + 1); + bool argsReplaced = !replacedArg.isEmpty() || !tmp->m_argTypeReplaced.isEmpty(); + if ((!argsReplaced && typesAreEqual(tmp->m_argType, argType)) + || (argsReplaced && replacedArg == tmp->argumentTypeReplaced())) { + tmp->addOverload(func); + overloadData = tmp; + } + } + } + + if (!overloadData) { + overloadData = new OverloadData(m_headOverloadData, func, argType, m_argPos + 1); + overloadData->m_previousOverloadData = this; + overloadData->m_generator = this->m_generator; + QString typeReplaced = func->typeReplaced(arg.argumentIndex() + 1); + + if (!typeReplaced.isEmpty()) + overloadData->m_argTypeReplaced = typeReplaced; + m_nextOverloadData.append(overloadData); + } + + return overloadData; +} + +QStringList OverloadData::returnTypes() const +{ + QSet<QString> retTypes; + for (const AbstractMetaFunction *func : m_overloads) { + if (!func->typeReplaced(0).isEmpty()) + retTypes << func->typeReplaced(0); + else if (!func->argumentRemoved(0)) + retTypes << func->type().cppSignature(); + } + return retTypes.values(); +} + +bool OverloadData::hasNonVoidReturnType() const +{ + QStringList retTypes = returnTypes(); + return !retTypes.contains(QLatin1String("void")) || retTypes.size() > 1; +} + +bool OverloadData::hasVarargs() const +{ + for (const AbstractMetaFunction *func : m_overloads) { + AbstractMetaArgumentList args = func->arguments(); + if (args.size() > 1 && args.constLast().type().isVarargs()) + return true; + } + return false; +} + +bool OverloadData::hasAllowThread() const +{ + for (const AbstractMetaFunction *func : m_overloads) { + if (func->allowThread()) + return true; + } + return false; +} + +bool OverloadData::hasStaticFunction(const AbstractMetaFunctionList &overloads) +{ + for (const AbstractMetaFunction *func : qAsConst(overloads)) { + if (func->isStatic()) + return true; + } + return false; +} + +bool OverloadData::hasStaticFunction() const +{ + for (const AbstractMetaFunction *func : m_overloads) { + if (func->isStatic()) + return true; + } + return false; +} + +bool OverloadData::hasInstanceFunction(const AbstractMetaFunctionList &overloads) +{ + for (const AbstractMetaFunction *func : qAsConst(overloads)) { + if (!func->isStatic()) + return true; + } + return false; +} + +bool OverloadData::hasInstanceFunction() const +{ + for (const AbstractMetaFunction *func : m_overloads) { + if (!func->isStatic()) + return true; + } + return false; +} + +bool OverloadData::hasStaticAndInstanceFunctions(const AbstractMetaFunctionList &overloads) +{ + return OverloadData::hasStaticFunction(overloads) && OverloadData::hasInstanceFunction(overloads); +} + +bool OverloadData::hasStaticAndInstanceFunctions() const +{ + return OverloadData::hasStaticFunction() && OverloadData::hasInstanceFunction(); +} + +const AbstractMetaFunction *OverloadData::referenceFunction() const +{ + return m_overloads.constFirst(); +} + +const AbstractMetaArgument *OverloadData::argument(const AbstractMetaFunction *func) const +{ + if (isHeadOverloadData() || !m_overloads.contains(func)) + return nullptr; + + int argPos = 0; + int removed = 0; + for (int i = 0; argPos <= m_argPos; i++) { + if (func->argumentRemoved(i + 1)) + removed++; + else + argPos++; + } + + return &func->arguments().at(m_argPos + removed); +} + +OverloadDataList OverloadData::overloadDataOnPosition(OverloadData *overloadData, int argPos) const +{ + OverloadDataList overloadDataList; + if (overloadData->argPos() == argPos) { + overloadDataList.append(overloadData); + } else if (overloadData->argPos() < argPos) { + const OverloadDataList &data = overloadData->nextOverloadData(); + for (OverloadData *pd : data) + overloadDataList += overloadDataOnPosition(pd, argPos); + } + return overloadDataList; +} + +OverloadDataList OverloadData::overloadDataOnPosition(int argPos) const +{ + OverloadDataList overloadDataList; + overloadDataList += overloadDataOnPosition(m_headOverloadData, argPos); + return overloadDataList; +} + +bool OverloadData::nextArgumentHasDefaultValue() const +{ + for (OverloadData *overloadData : m_nextOverloadData) { + if (overloadData->getFunctionWithDefaultValue()) + return true; + } + return false; +} + +static OverloadData *_findNextArgWithDefault(OverloadData *overloadData) +{ + if (overloadData->getFunctionWithDefaultValue()) + return overloadData; + + OverloadData *result = nullptr; + const OverloadDataList &data = overloadData->nextOverloadData(); + for (OverloadData *odata : data) { + OverloadData *tmp = _findNextArgWithDefault(odata); + if (!result || (tmp && result->argPos() > tmp->argPos())) + result = tmp; + } + return result; +} + +OverloadData *OverloadData::findNextArgWithDefault() +{ + return _findNextArgWithDefault(this); +} + +bool OverloadData::isFinalOccurrence(const AbstractMetaFunction *func) const +{ + for (const OverloadData *pd : m_nextOverloadData) { + if (pd->overloads().contains(func)) + return false; + } + return true; +} + +OverloadData::MetaFunctionList OverloadData::overloadsWithoutRepetition() const +{ + MetaFunctionList overloads = m_overloads; + for (const AbstractMetaFunction *func : m_overloads) { + if (func->minimalSignature().endsWith(QLatin1String("const"))) + continue; + for (const AbstractMetaFunction *f : qAsConst(overloads)) { + if ((func->minimalSignature() + QLatin1String("const")) == f->minimalSignature()) { + overloads.removeOne(f); + break; + } + } + } + return overloads; +} + +const AbstractMetaFunction *OverloadData::getFunctionWithDefaultValue() const +{ + for (const AbstractMetaFunction *func : m_overloads) { + int removedArgs = 0; + for (int i = 0; i <= m_argPos + removedArgs; i++) { + if (func->argumentRemoved(i + 1)) + removedArgs++; + } + if (func->arguments().at(m_argPos + removedArgs).hasDefaultValueExpression()) + return func; + } + return nullptr; +} + +QVector<int> OverloadData::invalidArgumentLengths() const +{ + QSet<int> validArgLengths; + + for (const AbstractMetaFunction *func : qAsConst(m_headOverloadData->m_overloads)) { + const AbstractMetaArgumentList args = func->arguments(); + int offset = 0; + for (int i = 0; i < args.size(); ++i) { + if (func->argumentRemoved(i+1)) { + offset++; + } else { + if (args.at(i).hasDefaultValueExpression()) + validArgLengths << i-offset; + } + } + validArgLengths << args.size() - offset; + } + + QVector<int> invalidArgLengths; + for (int i = minArgs() + 1; i < maxArgs(); i++) { + if (!validArgLengths.contains(i)) + invalidArgLengths.append(i); + } + + return invalidArgLengths; +} + +int OverloadData::numberOfRemovedArguments(const AbstractMetaFunction *func, int finalArgPos) +{ + int removed = 0; + if (finalArgPos < 0) { + for (int i = 0; i < func->arguments().size(); i++) { + if (func->argumentRemoved(i + 1)) + removed++; + } + } else { + for (int i = 0; i < finalArgPos + removed; i++) { + if (func->argumentRemoved(i + 1)) + removed++; + } + } + return removed; +} + +QPair<int, int> OverloadData::getMinMaxArguments(const AbstractMetaFunctionList &overloads) +{ + int minArgs = 10000; + int maxArgs = 0; + for (const AbstractMetaFunction *func : overloads) { + int origNumArgs = func->arguments().size(); + int removed = numberOfRemovedArguments(func); + int numArgs = origNumArgs - removed; + if (maxArgs < numArgs) + maxArgs = numArgs; + if (minArgs > numArgs) + minArgs = numArgs; + for (int j = 0; j < origNumArgs; j++) { + if (func->argumentRemoved(j + 1)) + continue; + int fixedArgIndex = j - removed; + if (fixedArgIndex < minArgs && func->arguments().at(j).hasDefaultValueExpression()) + minArgs = fixedArgIndex; + } + } + return {minArgs, maxArgs}; +} + +bool OverloadData::isSingleArgument(const AbstractMetaFunctionList &overloads) +{ + bool singleArgument = true; + for (const AbstractMetaFunction *func : overloads) { + if (func->arguments().size() - numberOfRemovedArguments(func) != 1) { + singleArgument = false; + break; + } + } + return singleArgument; +} + +void OverloadData::dumpGraph(const QString &filename) const +{ + QFile file(filename); + if (file.open(QFile::WriteOnly)) { + QTextStream s(&file); + s << m_headOverloadData->dumpGraph(); + } +} + +static inline QString toHtml(QString s) +{ + s.replace(QLatin1Char('<'), QLatin1String("<")); + s.replace(QLatin1Char('>'), QLatin1String(">")); + s.replace(QLatin1Char('&'), QLatin1String("&")); + return s; +} + +QString OverloadData::dumpGraph() const +{ + Indentor INDENT; + Indentation indent(INDENT); + QString result; + QTextStream s(&result); + if (m_argPos == -1) { + const AbstractMetaFunction *rfunc = referenceFunction(); + s << "digraph OverloadedFunction {\n"; + s << INDENT << "graph [fontsize=12 fontname=freemono labelloc=t splines=true overlap=false rankdir=LR];\n"; + + // Shows all function signatures + s << "legend [fontsize=9 fontname=freemono shape=rect label=\""; + for (const AbstractMetaFunction *func : m_overloads) { + s << "f" << functionNumber(func) << " : " + << toHtml(func->type().cppSignature()) + << ' ' << toHtml(func->minimalSignature()) << "\\l"; + } + s << "\"];\n"; + + // Function box title + s << INDENT << '"' << rfunc->name() << "\" [shape=plaintext style=\"filled,bold\" margin=0 fontname=freemono fillcolor=white penwidth=1 "; + s << "label=<<table border=\"0\" cellborder=\"0\" cellpadding=\"3\" bgcolor=\"white\">"; + s << "<tr><td bgcolor=\"black\" align=\"center\" cellpadding=\"6\" colspan=\"2\"><font color=\"white\">"; + if (rfunc->ownerClass()) + s << rfunc->ownerClass()->name() << "::"; + s << toHtml(rfunc->name()) << "</font>"; + if (rfunc->isVirtual()) { + s << "<br/><font color=\"white\" point-size=\"10\"><<"; + if (rfunc->isAbstract()) + s << "pure "; + s << "virtual>></font>"; + } + s << "</td></tr>"; + + // Function return type + s << "<tr><td bgcolor=\"gray\" align=\"right\">original type</td><td bgcolor=\"gray\" align=\"left\">" + << toHtml(rfunc->type().cppSignature()) + << "</td></tr>"; + + // Shows type changes for all function signatures + for (const AbstractMetaFunction *func : m_overloads) { + if (func->typeReplaced(0).isEmpty()) + continue; + s << "<tr><td bgcolor=\"gray\" align=\"right\">f" << functionNumber(func); + s << "-type</td><td bgcolor=\"gray\" align=\"left\">"; + s << toHtml(func->typeReplaced(0)) << "</td></tr>"; + } + + // Minimum and maximum number of arguments + s << "<tr><td bgcolor=\"gray\" align=\"right\">minArgs</td><td bgcolor=\"gray\" align=\"left\">"; + s << minArgs() << "</td></tr>"; + s << "<tr><td bgcolor=\"gray\" align=\"right\">maxArgs</td><td bgcolor=\"gray\" align=\"left\">"; + s << maxArgs() << "</td></tr>"; + + if (rfunc->ownerClass()) { + if (rfunc->implementingClass() != rfunc->ownerClass()) + s << "<tr><td align=\"right\">implementor</td><td align=\"left\">" << rfunc->implementingClass()->name() << "</td></tr>"; + if (rfunc->declaringClass() != rfunc->ownerClass() && rfunc->declaringClass() != rfunc->implementingClass()) + s << "<tr><td align=\"right\">declarator</td><td align=\"left\">" << rfunc->declaringClass()->name() << "</td></tr>"; + } + + // Overloads for the signature to present point + s << "<tr><td bgcolor=\"gray\" align=\"right\">overloads</td><td bgcolor=\"gray\" align=\"left\">"; + for (const AbstractMetaFunction *func : m_overloads) + s << 'f' << functionNumber(func) << ' '; + s << "</td></tr>"; + + s << "</table>> ];\n"; + + for (const OverloadData *pd : m_nextOverloadData) + s << INDENT << '"' << rfunc->name() << "\" -> " << pd->dumpGraph(); + + s << "}\n"; + } else { + QString argId = QLatin1String("arg_") + QString::number(quintptr(this)); + s << argId << ";\n"; + + s << INDENT << '"' << argId << "\" [shape=\"plaintext\" style=\"filled,bold\" margin=\"0\" fontname=\"freemono\" fillcolor=\"white\" penwidth=1 "; + s << "label=<<table border=\"0\" cellborder=\"0\" cellpadding=\"3\" bgcolor=\"white\">"; + + // Argument box title + s << "<tr><td bgcolor=\"black\" align=\"left\" cellpadding=\"2\" colspan=\"2\">"; + s << "<font color=\"white\" point-size=\"11\">arg #" << argPos() << "</font></td></tr>"; + + // Argument type information + QString type = hasArgumentTypeReplace() ? argumentTypeReplaced() : argType().cppSignature(); + s << "<tr><td bgcolor=\"gray\" align=\"right\">type</td><td bgcolor=\"gray\" align=\"left\">"; + s << toHtml(type) << "</td></tr>"; + if (hasArgumentTypeReplace()) { + s << "<tr><td bgcolor=\"gray\" align=\"right\">orig. type</td><td bgcolor=\"gray\" align=\"left\">"; + s << toHtml(argType().cppSignature()) << "</td></tr>"; + } + + // Overloads for the signature to present point + s << "<tr><td bgcolor=\"gray\" align=\"right\">overloads</td><td bgcolor=\"gray\" align=\"left\">"; + for (const AbstractMetaFunction *func : m_overloads) + s << 'f' << functionNumber(func) << ' '; + s << "</td></tr>"; + + // Show default values (original and modified) for various functions + for (const AbstractMetaFunction *func : m_overloads) { + const AbstractMetaArgument *arg = argument(func); + if (!arg) + continue; + QString argDefault = arg->defaultValueExpression(); + if (!argDefault.isEmpty() || + argDefault != arg->originalDefaultValueExpression()) { + s << "<tr><td bgcolor=\"gray\" align=\"right\">f" << functionNumber(func); + s << "-default</td><td bgcolor=\"gray\" align=\"left\">"; + s << argDefault << "</td></tr>"; + } + if (argDefault != arg->originalDefaultValueExpression()) { + s << "<tr><td bgcolor=\"gray\" align=\"right\">f" << functionNumber(func); + s << "-orig-default</td><td bgcolor=\"gray\" align=\"left\">"; + s << arg->originalDefaultValueExpression() << "</td></tr>"; + } + } + + s << "</table>>];\n"; + + for (const OverloadData *pd : m_nextOverloadData) + s << INDENT << argId << " -> " << pd->dumpGraph(); + } + return result; +} + +int OverloadData::functionNumber(const AbstractMetaFunction *func) const +{ + return m_headOverloadData->m_overloads.indexOf(func); +} + +OverloadData::~OverloadData() +{ + while (!m_nextOverloadData.isEmpty()) + delete m_nextOverloadData.takeLast(); +} + +bool OverloadData::hasArgumentTypeReplace() const +{ + return !m_argTypeReplaced.isEmpty(); +} + +QString OverloadData::argumentTypeReplaced() const +{ + return m_argTypeReplaced; +} + +bool OverloadData::hasArgumentWithDefaultValue(const AbstractMetaFunctionList &overloads) +{ + if (OverloadData::getMinMaxArguments(overloads).second == 0) + return false; + for (const AbstractMetaFunction *func : overloads) { + if (hasArgumentWithDefaultValue(func)) + return true; + } + return false; +} + +bool OverloadData::hasArgumentWithDefaultValue() const +{ + if (maxArgs() == 0) + return false; + for (const AbstractMetaFunction *func : m_overloads) { + if (hasArgumentWithDefaultValue(func)) + return true; + } + return false; +} + +bool OverloadData::hasArgumentWithDefaultValue(const AbstractMetaFunction *func) +{ + const AbstractMetaArgumentList &arguments = func->arguments(); + for (const AbstractMetaArgument &arg : arguments) { + if (func->argumentRemoved(arg.argumentIndex() + 1)) + continue; + if (arg.hasDefaultValueExpression()) + return true; + } + return false; +} + +AbstractMetaArgumentList OverloadData::getArgumentsWithDefaultValues(const AbstractMetaFunction *func) +{ + AbstractMetaArgumentList args; + const AbstractMetaArgumentList &arguments = func->arguments(); + for (const AbstractMetaArgument &arg : arguments) { + if (!arg.hasDefaultValueExpression() + || func->argumentRemoved(arg.argumentIndex() + 1)) + continue; + args << arg; + } + return args; +} + +#ifndef QT_NO_DEBUG_STREAM +void OverloadData::formatDebug(QDebug &d) const +{ + const int count = m_overloads.size(); + d << "argType=" << m_argType << ", minArgs=" << m_minArgs << ", maxArgs=" << m_maxArgs + << ", argPos=" << m_argPos << ", argTypeReplaced=\"" << m_argTypeReplaced + << "\", overloads[" << count << "]=("; + const int oldVerbosity = d.verbosity(); + d.setVerbosity(3); + for (int i = 0; i < count; ++i) { + if (i) + d << '\n'; + d << m_overloads.at(i); + } + d << ')'; + d.setVerbosity(oldVerbosity); +} + +QDebug operator<<(QDebug d, const OverloadData *od) +{ + QDebugStateSaver saver(d); + d.noquote(); + d.nospace(); + d << "OverloadData("; + if (od) + od->formatDebug(d); + else + d << '0'; + d << ')'; + return d; +} +#endif // !QT_NO_DEBUG_STREAM diff --git a/sources/shiboken6/generator/shiboken/overloaddata.h b/sources/shiboken6/generator/shiboken/overloaddata.h new file mode 100644 index 000000000..2507c3213 --- /dev/null +++ b/sources/shiboken6/generator/shiboken/overloaddata.h @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef OVERLOADDATA_H +#define OVERLOADDATA_H + +#include <abstractmetalang_typedefs.h> +#include <QtCore/QBitArray> +#include <QtCore/QVector> + +QT_FORWARD_DECLARE_CLASS(QDebug) + +class ShibokenGenerator; + +class OverloadData; +using OverloadDataList = QVector<OverloadData *>; + +class OverloadData +{ +public: + using MetaFunctionList = QVector<const AbstractMetaFunction *>; + + OverloadData(const AbstractMetaFunctionList &overloads, const ShibokenGenerator *generator); + ~OverloadData(); + + int minArgs() const { return m_headOverloadData->m_minArgs; } + int maxArgs() const { return m_headOverloadData->m_maxArgs; } + int argPos() const { return m_argPos; } + + const AbstractMetaType &argType() const { return m_argType; } + + /// Returns a string list containing all the possible return types (including void) for the current OverloadData. + QStringList returnTypes() const; + + /// Returns true if any of the overloads for the current OverloadData has a return type different from void. + bool hasNonVoidReturnType() const; + + /// Returns true if any of the overloads for the current OverloadData has a varargs argument. + bool hasVarargs() const; + + /// Returns true if any of the overloads for the current OverloadData allows threads when called. + bool hasAllowThread() const; + + /// Returns true if any of the overloads for the current OverloadData is static. + bool hasStaticFunction() const; + + /// Returns true if any of the overloads passed as argument is static. + static bool hasStaticFunction(const AbstractMetaFunctionList &overloads); + + /// Returns true if any of the overloads for the current OverloadData is not static. + bool hasInstanceFunction() const; + + /// Returns true if any of the overloads passed as argument is not static. + static bool hasInstanceFunction(const AbstractMetaFunctionList &overloads); + + /// Returns true if among the overloads for the current OverloadData there are static and non-static methods altogether. + bool hasStaticAndInstanceFunctions() const; + + /// Returns true if among the overloads passed as argument there are static and non-static methods altogether. + static bool hasStaticAndInstanceFunctions(const AbstractMetaFunctionList &overloads); + + const AbstractMetaFunction *referenceFunction() const; + const AbstractMetaArgument *argument(const AbstractMetaFunction *func) const; + OverloadDataList overloadDataOnPosition(int argPos) const; + + bool isHeadOverloadData() const { return this == m_headOverloadData; } + + /// Returns the root OverloadData object that represents all the overloads. + OverloadData *headOverloadData() const { return m_headOverloadData; } + + /// Returns the function that has a default value at the current OverloadData argument position, otherwise returns null. + const AbstractMetaFunction *getFunctionWithDefaultValue() const; + + bool nextArgumentHasDefaultValue() const; + /// Returns the nearest occurrence, including this instance, of an argument with a default value. + OverloadData *findNextArgWithDefault(); + bool isFinalOccurrence(const AbstractMetaFunction *func) const; + + /// Returns the list of overloads removing repeated constant functions (ex.: "foo()" and "foo()const", the second is removed). + MetaFunctionList overloadsWithoutRepetition() const; + const MetaFunctionList &overloads() const { return m_overloads; } + OverloadDataList nextOverloadData() const { return m_nextOverloadData; } + OverloadData *previousOverloadData() const { return m_previousOverloadData; } + + QVector<int> invalidArgumentLengths() const; + + static int numberOfRemovedArguments(const AbstractMetaFunction *func, int finalArgPos = -1); + static QPair<int, int> getMinMaxArguments(const AbstractMetaFunctionList &overloads); + /// Returns true if all overloads have no more than one argument. + static bool isSingleArgument(const AbstractMetaFunctionList &overloads); + + void dumpGraph(const QString &filename) const; + QString dumpGraph() const; + + bool hasArgumentTypeReplace() const; + QString argumentTypeReplaced() const; + + bool hasArgumentWithDefaultValue() const; + static bool hasArgumentWithDefaultValue(const AbstractMetaFunctionList &overloads); + static bool hasArgumentWithDefaultValue(const AbstractMetaFunction *func); + + /// Returns a list of function arguments which have default values and were not removed. + static AbstractMetaArgumentList getArgumentsWithDefaultValues(const AbstractMetaFunction *func); + +#ifndef QT_NO_DEBUG_STREAM + void formatDebug(QDebug &) const; +#endif + +private: + OverloadData(OverloadData *headOverloadData, const AbstractMetaFunction *func, + const AbstractMetaType &argType, int argPos); + + void addOverload(const AbstractMetaFunction *func); + OverloadData *addOverloadData(const AbstractMetaFunction *func, const AbstractMetaArgument &arg); + + void sortNextOverloads(); + bool sortByOverloadNumberModification(); + + int functionNumber(const AbstractMetaFunction *func) const; + OverloadDataList overloadDataOnPosition(OverloadData *overloadData, int argPos) const; + + int m_minArgs; + int m_maxArgs; + int m_argPos; + AbstractMetaType m_argType; + QString m_argTypeReplaced; + MetaFunctionList m_overloads; + + OverloadData *m_headOverloadData; + OverloadDataList m_nextOverloadData; + OverloadData *m_previousOverloadData; + const ShibokenGenerator *m_generator; +}; + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug, const OverloadData *); +#endif + +#endif // OVERLOADDATA_H diff --git a/sources/shiboken6/generator/shiboken/shibokengenerator.cpp b/sources/shiboken6/generator/shiboken/shibokengenerator.cpp new file mode 100644 index 000000000..5382b3a9f --- /dev/null +++ b/sources/shiboken6/generator/shiboken/shibokengenerator.cpp @@ -0,0 +1,2884 @@ +/**************************************************************************** +** +** 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 "shibokengenerator.h" +#include "ctypenames.h" +#include <abstractmetalang.h> +#include <messages.h> +#include "overloaddata.h" +#include "propertyspec.h" +#include <reporthandler.h> +#include <typedatabase.h> +#include <abstractmetabuilder.h> +#include <iostream> + +#include <QtCore/QDir> +#include <QtCore/QDebug> +#include <QtCore/QRegularExpression> +#include <limits> +#include <memory> + +static const char AVOID_PROTECTED_HACK[] = "avoid-protected-hack"; +static const char PARENT_CTOR_HEURISTIC[] = "enable-parent-ctor-heuristic"; +static const char RETURN_VALUE_HEURISTIC[] = "enable-return-value-heuristic"; +static const char ENABLE_PYSIDE_EXTENSIONS[] = "enable-pyside-extensions"; +static const char DISABLE_VERBOSE_ERROR_MESSAGES[] = "disable-verbose-error-messages"; +static const char USE_ISNULL_AS_NB_NONZERO[] = "use-isnull-as-nb_nonzero"; +static const char WRAPPER_DIAGNOSTICS[] = "wrapper-diagnostics"; + +const char *CPP_ARG = "cppArg"; +const char *CPP_ARG_REMOVED = "removed_cppArg"; +const char *CPP_RETURN_VAR = "cppResult"; +const char *CPP_SELF_VAR = "cppSelf"; +const char *NULL_PTR = "nullptr"; +const char *PYTHON_ARG = "pyArg"; +const char *PYTHON_ARGS = "pyArgs"; +const char *PYTHON_OVERRIDE_VAR = "pyOverride"; +const char *PYTHON_RETURN_VAR = "pyResult"; +const char *PYTHON_TO_CPP_VAR = "pythonToCpp"; +const char *SMART_POINTER_GETTER = "kSmartPointerGetter"; + +const char *CONV_RULE_OUT_VAR_SUFFIX = "_out"; +const char *BEGIN_ALLOW_THREADS = + "PyThreadState *_save = PyEval_SaveThread(); // Py_BEGIN_ALLOW_THREADS"; +const char *END_ALLOW_THREADS = "PyEval_RestoreThread(_save); // Py_END_ALLOW_THREADS"; + +//static void dumpFunction(AbstractMetaFunctionList lst); + +QHash<QString, QString> ShibokenGenerator::m_pythonPrimitiveTypeName = QHash<QString, QString>(); +QHash<QString, QString> ShibokenGenerator::m_pythonOperators = QHash<QString, QString>(); +QHash<QString, QString> ShibokenGenerator::m_formatUnits = QHash<QString, QString>(); +QHash<QString, QString> ShibokenGenerator::m_tpFuncs = QHash<QString, QString>(); +QStringList ShibokenGenerator::m_knownPythonTypes = QStringList(); + +static QRegularExpression placeHolderRegex(int index) +{ + return QRegularExpression(QLatin1Char('%') + QString::number(index) + QStringLiteral("\\b")); +} + +// Return a prefix to fully qualify value, eg: +// resolveScopePrefix("Class::NestedClass::Enum::Value1", "Enum::Value1") +// -> "Class::NestedClass::") +static QString resolveScopePrefix(const QStringList &scopeList, const QString &value) +{ + QString name; + for (int i = scopeList.size() - 1 ; i >= 0; --i) { + const QString prefix = scopeList.at(i) + QLatin1String("::"); + if (value.startsWith(prefix)) + name.clear(); + else + name.prepend(prefix); + } + return name; +} + +static inline QStringList splitClassScope(const AbstractMetaClass *scope) +{ + return scope->qualifiedCppName().split(QLatin1String("::"), Qt::SkipEmptyParts); +} + +static QString resolveScopePrefix(const AbstractMetaClass *scope, const QString &value) +{ + return scope + ? resolveScopePrefix(splitClassScope(scope), value) + : QString(); +} + +static QString resolveScopePrefix(const AbstractMetaEnum *metaEnum, + const QString &value) +{ + QStringList parts; + if (const AbstractMetaClass *scope = metaEnum->enclosingClass()) + parts.append(splitClassScope(scope)); + // Fully qualify the value which is required for C++ 11 enum classes. + if (!metaEnum->isAnonymous()) + parts.append(metaEnum->name()); + return resolveScopePrefix(parts, value); +} + +struct GeneratorClassInfoCacheEntry +{ + ShibokenGenerator::FunctionGroups functionGroups; + bool needsGetattroFunction = false; +}; + +using GeneratorClassInfoCache = QHash<const AbstractMetaClass *, GeneratorClassInfoCacheEntry>; + +Q_GLOBAL_STATIC(GeneratorClassInfoCache, generatorClassInfoCache) + +ShibokenGenerator::ShibokenGenerator() +{ + if (m_pythonPrimitiveTypeName.isEmpty()) + ShibokenGenerator::initPrimitiveTypesCorrespondences(); + + if (m_tpFuncs.isEmpty()) + ShibokenGenerator::clearTpFuncs(); + + if (m_knownPythonTypes.isEmpty()) + ShibokenGenerator::initKnownPythonTypes(); + + m_metaTypeFromStringCache = AbstractMetaTypeCache(); + + m_typeSystemConvName[TypeSystemCheckFunction] = QLatin1String("checkType"); + m_typeSystemConvName[TypeSystemIsConvertibleFunction] = QLatin1String("isConvertible"); + m_typeSystemConvName[TypeSystemToCppFunction] = QLatin1String("toCpp"); + m_typeSystemConvName[TypeSystemToPythonFunction] = QLatin1String("toPython"); + + const char CHECKTYPE_REGEX[] = R"(%CHECKTYPE\[([^\[]*)\]\()"; + const char ISCONVERTIBLE_REGEX[] = R"(%ISCONVERTIBLE\[([^\[]*)\]\()"; + const char CONVERTTOPYTHON_REGEX[] = R"(%CONVERTTOPYTHON\[([^\[]*)\]\()"; + // Capture a '*' leading the variable name into the target + // so that "*valuePtr = %CONVERTTOCPP..." works as expected. + const char CONVERTTOCPP_REGEX[] = + R"((\*?%?[a-zA-Z_][\w\.]*(?:\[[^\[^<^>]+\])*)(?:\s+)=(?:\s+)%CONVERTTOCPP\[([^\[]*)\]\()"; + m_typeSystemConvRegEx[TypeSystemCheckFunction] = QRegularExpression(QLatin1String(CHECKTYPE_REGEX)); + m_typeSystemConvRegEx[TypeSystemIsConvertibleFunction] = QRegularExpression(QLatin1String(ISCONVERTIBLE_REGEX)); + m_typeSystemConvRegEx[TypeSystemToPythonFunction] = QRegularExpression(QLatin1String(CONVERTTOPYTHON_REGEX)); + m_typeSystemConvRegEx[TypeSystemToCppFunction] = QRegularExpression(QLatin1String(CONVERTTOCPP_REGEX)); +} + +ShibokenGenerator::~ShibokenGenerator() = default; + +void ShibokenGenerator::clearTpFuncs() +{ + m_tpFuncs.insert(QLatin1String("__str__"), QString()); + m_tpFuncs.insert(QLatin1String("__repr__"), QString()); + m_tpFuncs.insert(QLatin1String("__iter__"), QString()); + m_tpFuncs.insert(QLatin1String("__next__"), QString()); +} + +void ShibokenGenerator::initPrimitiveTypesCorrespondences() +{ + // Python primitive types names + m_pythonPrimitiveTypeName.clear(); + + // PyBool + m_pythonPrimitiveTypeName.insert(QLatin1String("bool"), QLatin1String("PyBool")); + + const char *charTypes[] = { + "char", "signed char", "unsigned char" + }; + for (const char *charType : charTypes) + m_pythonPrimitiveTypeName.insert(QLatin1String(charType), QStringLiteral("SbkChar")); + + // PyInt + const char *intTypes[] = { + "int", "signed int", "uint", "unsigned int", + "short", "ushort", "signed short", "signed short int", + "unsigned short", "unsigned short", "unsigned short int", + "long" + }; + for (const char *intType : intTypes) + m_pythonPrimitiveTypeName.insert(QLatin1String(intType), QStringLiteral("PyInt")); + + // PyFloat + m_pythonPrimitiveTypeName.insert(doubleT(), QLatin1String("PyFloat")); + m_pythonPrimitiveTypeName.insert(floatT(), QLatin1String("PyFloat")); + + // PyLong + const char *longTypes[] = { + "unsigned long", "signed long", "ulong", "unsigned long int", + "long long", "__int64", + "unsigned long long", "unsigned __int64", "size_t" + }; + for (const char *longType : longTypes) + m_pythonPrimitiveTypeName.insert(QLatin1String(longType), QStringLiteral("PyLong")); + + // Python operators + m_pythonOperators.clear(); + + // call operator + m_pythonOperators.insert(QLatin1String("operator()"), QLatin1String("call")); + + // Arithmetic operators + m_pythonOperators.insert(QLatin1String("operator+"), QLatin1String("add")); + m_pythonOperators.insert(QLatin1String("operator-"), QLatin1String("sub")); + m_pythonOperators.insert(QLatin1String("operator*"), QLatin1String("mul")); + m_pythonOperators.insert(QLatin1String("operator/"), QLatin1String("div")); + m_pythonOperators.insert(QLatin1String("operator%"), QLatin1String("mod")); + + // Inplace arithmetic operators + m_pythonOperators.insert(QLatin1String("operator+="), QLatin1String("iadd")); + m_pythonOperators.insert(QLatin1String("operator-="), QLatin1String("isub")); + m_pythonOperators.insert(QLatin1String("operator++"), QLatin1String("iadd")); + m_pythonOperators.insert(QLatin1String("operator--"), QLatin1String("isub")); + m_pythonOperators.insert(QLatin1String("operator*="), QLatin1String("imul")); + m_pythonOperators.insert(QLatin1String("operator/="), QLatin1String("idiv")); + m_pythonOperators.insert(QLatin1String("operator%="), QLatin1String("imod")); + + // Bitwise operators + m_pythonOperators.insert(QLatin1String("operator&"), QLatin1String("and")); + m_pythonOperators.insert(QLatin1String("operator^"), QLatin1String("xor")); + m_pythonOperators.insert(QLatin1String("operator|"), QLatin1String("or")); + m_pythonOperators.insert(QLatin1String("operator<<"), QLatin1String("lshift")); + m_pythonOperators.insert(QLatin1String("operator>>"), QLatin1String("rshift")); + m_pythonOperators.insert(QLatin1String("operator~"), QLatin1String("invert")); + + // Inplace bitwise operators + m_pythonOperators.insert(QLatin1String("operator&="), QLatin1String("iand")); + m_pythonOperators.insert(QLatin1String("operator^="), QLatin1String("ixor")); + m_pythonOperators.insert(QLatin1String("operator|="), QLatin1String("ior")); + m_pythonOperators.insert(QLatin1String("operator<<="), QLatin1String("ilshift")); + m_pythonOperators.insert(QLatin1String("operator>>="), QLatin1String("irshift")); + + // Comparison operators + m_pythonOperators.insert(QLatin1String("operator=="), QLatin1String("eq")); + m_pythonOperators.insert(QLatin1String("operator!="), QLatin1String("ne")); + m_pythonOperators.insert(QLatin1String("operator<"), QLatin1String("lt")); + m_pythonOperators.insert(QLatin1String("operator>"), QLatin1String("gt")); + m_pythonOperators.insert(QLatin1String("operator<="), QLatin1String("le")); + m_pythonOperators.insert(QLatin1String("operator>="), QLatin1String("ge")); + + // Initialize format units for C++->Python->C++ conversion + m_formatUnits.clear(); + m_formatUnits.insert(QLatin1String("char"), QLatin1String("b")); + m_formatUnits.insert(QLatin1String("unsigned char"), QLatin1String("B")); + m_formatUnits.insert(intT(), QLatin1String("i")); + m_formatUnits.insert(QLatin1String("unsigned int"), QLatin1String("I")); + m_formatUnits.insert(shortT(), QLatin1String("h")); + m_formatUnits.insert(unsignedShortT(), QLatin1String("H")); + m_formatUnits.insert(longT(), QLatin1String("l")); + m_formatUnits.insert(unsignedLongLongT(), QLatin1String("k")); + m_formatUnits.insert(longLongT(), QLatin1String("L")); + m_formatUnits.insert(QLatin1String("__int64"), QLatin1String("L")); + m_formatUnits.insert(unsignedLongLongT(), QLatin1String("K")); + m_formatUnits.insert(QLatin1String("unsigned __int64"), QLatin1String("K")); + m_formatUnits.insert(doubleT(), QLatin1String("d")); + m_formatUnits.insert(floatT(), QLatin1String("f")); +} + +void ShibokenGenerator::initKnownPythonTypes() +{ + m_knownPythonTypes.clear(); + m_knownPythonTypes << QLatin1String("PyBool") << QLatin1String("PyInt") + << QLatin1String("PyFloat") << QLatin1String("PyLong") << QLatin1String("PyObject") + << QLatin1String("PyString") << QLatin1String("PyBuffer") << QLatin1String("PySequence") + << QLatin1String("PyTuple") << QLatin1String("PyList") << QLatin1String("PyDict") + << QLatin1String("PyObject*") << QLatin1String("PyObject *") << QLatin1String("PyTupleObject*"); +} + +QString ShibokenGenerator::translateTypeForWrapperMethod(const AbstractMetaType &cType, + const AbstractMetaClass *context, + Options options) const +{ + if (cType.isArray()) + return translateTypeForWrapperMethod(*cType.arrayElementType(), context, options) + QLatin1String("[]"); + + if (avoidProtectedHack() && cType.isEnum()) { + const AbstractMetaEnum *metaEnum = findAbstractMetaEnum(cType); + if (metaEnum && metaEnum->isProtected()) + return protectedEnumSurrogateName(metaEnum); + } + + return translateType(cType, context, options); +} + +bool ShibokenGenerator::shouldGenerateCppWrapper(const AbstractMetaClass *metaClass) const +{ + if (metaClass->isNamespace() + || metaClass->attributes().testFlag(AbstractMetaAttributes::FinalCppClass) + || metaClass->typeEntry()->typeFlags().testFlag(ComplexTypeEntry::DisableWrapper)) { + return false; + } + bool result = metaClass->isPolymorphic() || metaClass->hasVirtualDestructor(); + if (avoidProtectedHack()) { + result = result || metaClass->hasProtectedFields() || metaClass->hasProtectedDestructor(); + if (!result && metaClass->hasProtectedFunctions()) { + int protectedFunctions = 0; + int protectedOperators = 0; + const AbstractMetaFunctionList &funcs = metaClass->functions(); + for (const AbstractMetaFunction *func : funcs) { + if (!func->isProtected() || func->isSignal() || func->isModifiedRemoved()) + continue; + if (func->isOperatorOverload()) + protectedOperators++; + else + protectedFunctions++; + } + result = result || (protectedFunctions > protectedOperators); + } + } else { + result = result && !metaClass->hasPrivateDestructor(); + } + return result; +} + +bool ShibokenGenerator::shouldWriteVirtualMethodNative(const AbstractMetaFunction *func) +{ + // PYSIDE-803: Extracted this because it is used multiple times. + const AbstractMetaClass *metaClass = func->ownerClass(); + return (!avoidProtectedHack() || !metaClass->hasPrivateDestructor()) + && ((func->isVirtual() || func->isAbstract()) + && (func->attributes() & AbstractMetaAttributes::FinalCppMethod) == 0); +} + +QString ShibokenGenerator::wrapperName(const AbstractMetaClass *metaClass) const +{ + Q_ASSERT(shouldGenerateCppWrapper(metaClass)); + QString result = metaClass->name(); + if (metaClass->enclosingClass()) // is a inner class + result.replace(QLatin1String("::"), QLatin1String("_")); + return result + QLatin1String("Wrapper"); +} + +QString ShibokenGenerator::fullPythonClassName(const AbstractMetaClass *metaClass) +{ + QString fullClassName = metaClass->name(); + const AbstractMetaClass *enclosing = metaClass->enclosingClass(); + while (enclosing) { + if (NamespaceTypeEntry::isVisibleScope(enclosing->typeEntry())) + fullClassName.prepend(enclosing->name() + QLatin1Char('.')); + enclosing = enclosing->enclosingClass(); + } + fullClassName.prepend(packageName() + QLatin1Char('.')); + return fullClassName; +} + +QString ShibokenGenerator::fullPythonFunctionName(const AbstractMetaFunction *func) +{ + QString funcName; + if (func->isOperatorOverload()) + funcName = ShibokenGenerator::pythonOperatorFunctionName(func); + else + funcName = func->name(); + if (func->ownerClass()) { + QString fullClassName = fullPythonClassName(func->ownerClass()); + if (func->isConstructor()) + funcName = fullClassName; + else + funcName.prepend(fullClassName + QLatin1Char('.')); + } + else { + funcName = packageName() + QLatin1Char('.') + func->name(); + } + return funcName; +} + +QString ShibokenGenerator::protectedEnumSurrogateName(const AbstractMetaEnum *metaEnum) +{ + return metaEnum->fullName().replace(QLatin1Char('.'), QLatin1Char('_')).replace(QLatin1String("::"), QLatin1String("_")) + QLatin1String("_Surrogate"); +} + +QString ShibokenGenerator::protectedFieldGetterName(const AbstractMetaField *field) +{ + return QStringLiteral("protected_%1_getter").arg(field->name()); +} + +QString ShibokenGenerator::protectedFieldSetterName(const AbstractMetaField *field) +{ + return QStringLiteral("protected_%1_setter").arg(field->name()); +} + +QString ShibokenGenerator::cpythonFunctionName(const AbstractMetaFunction *func) +{ + QString result; + + // PYSIDE-331: For inherited functions, we need to find the same labels. + // Therefore we use the implementing class. + if (func->implementingClass()) { + result = cpythonBaseName(func->implementingClass()->typeEntry()); + if (func->isConstructor()) { + result += QLatin1String("_Init"); + } else { + result += QLatin1String("Func_"); + if (func->isOperatorOverload()) + result += ShibokenGenerator::pythonOperatorFunctionName(func); + else + result += func->name(); + } + } else { + result = QLatin1String("Sbk") + moduleName() + QLatin1String("Module_") + func->name(); + } + + return result; +} + +QString ShibokenGenerator::cpythonMethodDefinitionName(const AbstractMetaFunction *func) +{ + if (!func->ownerClass()) + return QString(); + return QStringLiteral("%1Method_%2").arg(cpythonBaseName(func->ownerClass()->typeEntry()), func->name()); +} + +QString ShibokenGenerator::cpythonGettersSettersDefinitionName(const AbstractMetaClass *metaClass) +{ + return cpythonBaseName(metaClass) + QLatin1String("_getsetlist"); +} + +QString ShibokenGenerator::cpythonSetattroFunctionName(const AbstractMetaClass *metaClass) +{ + return cpythonBaseName(metaClass) + QLatin1String("_setattro"); +} + + +QString ShibokenGenerator::cpythonGetattroFunctionName(const AbstractMetaClass *metaClass) +{ + return cpythonBaseName(metaClass) + QLatin1String("_getattro"); +} + +QString ShibokenGenerator::cpythonGetterFunctionName(const QString &name, + const AbstractMetaClass *enclosingClass) +{ + return cpythonBaseName(enclosingClass) + QStringLiteral("_get_") + name; +} + +QString ShibokenGenerator::cpythonSetterFunctionName(const QString &name, + const AbstractMetaClass *enclosingClass) +{ + return cpythonBaseName(enclosingClass) + QStringLiteral("_set_") + name; +} + +QString ShibokenGenerator::cpythonGetterFunctionName(const AbstractMetaField *metaField) +{ + return cpythonGetterFunctionName(metaField->name(), metaField->enclosingClass()); +} + +QString ShibokenGenerator::cpythonSetterFunctionName(const AbstractMetaField *metaField) +{ + return cpythonSetterFunctionName(metaField->name(), metaField->enclosingClass()); +} + +QString ShibokenGenerator::cpythonGetterFunctionName(const QPropertySpec *property, + const AbstractMetaClass *metaClass) +{ + return cpythonGetterFunctionName(property->name(), metaClass); +} + +QString ShibokenGenerator::cpythonSetterFunctionName(const QPropertySpec *property, + const AbstractMetaClass *metaClass) +{ + return cpythonSetterFunctionName(property->name(), metaClass); +} + +static QString cpythonEnumFlagsName(const QString &moduleName, + const QString &qualifiedCppName) +{ + QString result = QStringLiteral("Sbk%1_%2").arg(moduleName, qualifiedCppName); + result.replace(QLatin1String("::"), QLatin1String("_")); + return result; +} + +// Return the scope for fully qualifying the enumeration including trailing "::". +static QString searchForEnumScope(const AbstractMetaClass *metaClass, const QString &value) +{ + if (!metaClass) + return QString(); + const AbstractMetaEnumList &enums = metaClass->enums(); + for (const AbstractMetaEnum *metaEnum : enums) { + if (metaEnum->findEnumValue(value)) + return resolveScopePrefix(metaEnum, value); + } + // PYSIDE-331: We need to also search the base classes. + QString ret = searchForEnumScope(metaClass->enclosingClass(), value); + if (ret.isEmpty()) + ret = searchForEnumScope(metaClass->baseClass(), value); + return ret; +} + +// Handle QFlags<> for guessScopeForDefaultValue() +QString ShibokenGenerator::guessScopeForDefaultFlagsValue(const AbstractMetaFunction *func, + const AbstractMetaArgument &arg, + const QString &value) const +{ + // Numeric values -> "Options(42)" + static const QRegularExpression numberRegEx(QStringLiteral("^\\d+$")); // Numbers to flags + Q_ASSERT(numberRegEx.isValid()); + if (numberRegEx.match(value).hasMatch()) { + QString typeName = translateTypeForWrapperMethod(arg.type(), func->implementingClass()); + if (arg.type().isConstant()) + typeName.remove(0, sizeof("const ") / sizeof(char) - 1); + switch (arg.type().referenceType()) { + case NoReference: + break; + case LValueReference: + typeName.chop(1); + break; + case RValueReference: + typeName.chop(2); + break; + } + return typeName + QLatin1Char('(') + value + QLatin1Char(')'); + } + + // "Options(Option1 | Option2)" -> "Options(Class::Enum::Option1 | Class::Enum::Option2)" + static const QRegularExpression enumCombinationRegEx(QStringLiteral("^([A-Za-z_][\\w:]*)\\(([^,\\(\\)]*)\\)$")); // FlagName(EnumItem|EnumItem|...) + Q_ASSERT(enumCombinationRegEx.isValid()); + const QRegularExpressionMatch match = enumCombinationRegEx.match(value); + if (match.hasMatch()) { + const QString expression = match.captured(2).trimmed(); + if (expression.isEmpty()) + return value; + const QStringList enumItems = expression.split(QLatin1Char('|')); + const QString scope = searchForEnumScope(func->implementingClass(), + enumItems.constFirst().trimmed()); + if (scope.isEmpty()) + return value; + QString result; + QTextStream str(&result); + str << match.captured(1) << '('; // Flag name + for (int i = 0, size = enumItems.size(); i < size; ++i) { + if (i) + str << '|'; + str << scope << enumItems.at(i).trimmed(); + } + str << ')'; + return result; + } + // A single flag "Option1" -> "Class::Enum::Option1" + return searchForEnumScope(func->implementingClass(), value) + value; +} + +/* + * This function uses some heuristics to find out the scope for a given + * argument default value since they must be fully qualified when used outside the class: + * class A { + * enum Enum { e1, e1 }; + * void foo(Enum e = e1); + * } + * should be qualified to: + * A::Enum cppArg0 = A::Enum::e1; + * + * New situations may arise in the future and + * this method should be updated, do it with care. + */ +QString ShibokenGenerator::guessScopeForDefaultValue(const AbstractMetaFunction *func, + const AbstractMetaArgument &arg) const +{ + QString value = arg.defaultValueExpression(); + + if (value.isEmpty() || value == QLatin1String("{}") + || arg.hasModifiedDefaultValueExpression() + || isPointer(arg.type())) { + return value; + } + + static const QRegularExpression enumValueRegEx(QStringLiteral("^([A-Za-z_]\\w*)?$")); + Q_ASSERT(enumValueRegEx.isValid()); + // Do not qualify macros by class name, eg QSGGeometry(..., int t = GL_UNSIGNED_SHORT); + static const QRegularExpression macroRegEx(QStringLiteral("^[A-Z_][A-Z0-9_]*$")); + Q_ASSERT(macroRegEx.isValid()); + if (arg.type().isPrimitive() && macroRegEx.match(value).hasMatch()) + return value; + + QString prefix; + if (arg.type().isEnum()) { + if (const AbstractMetaEnum *metaEnum = findAbstractMetaEnum(arg.type())) + prefix = resolveScopePrefix(metaEnum, value); + } else if (arg.type().isFlags()) { + value = guessScopeForDefaultFlagsValue(func, arg, value); + } else if (arg.type().typeEntry()->isValue()) { + const AbstractMetaClass *metaClass = AbstractMetaClass::findClass(classes(), + arg.type().typeEntry()); + if (enumValueRegEx.match(value).hasMatch() && value != QLatin1String("NULL")) + prefix = resolveScopePrefix(metaClass, value); + } else if (arg.type().isPrimitive() && arg.type().name() == intT()) { + if (enumValueRegEx.match(value).hasMatch() && func->implementingClass()) + prefix = resolveScopePrefix(func->implementingClass(), value); + } else if (arg.type().isPrimitive()) { + static const QRegularExpression unknowArgumentRegEx(QStringLiteral("^(?:[A-Za-z_][\\w:]*\\()?([A-Za-z_]\\w*)(?:\\))?$")); // [PrimitiveType(] DESIREDNAME [)] + Q_ASSERT(unknowArgumentRegEx.isValid()); + const QRegularExpressionMatch match = unknowArgumentRegEx.match(value); + if (match.hasMatch() && func->implementingClass()) { + const AbstractMetaFieldList &fields = func->implementingClass()->fields(); + for (const AbstractMetaField *field : fields) { + if (match.captured(1).trimmed() == field->name()) { + QString fieldName = field->name(); + if (field->isStatic()) { + prefix = resolveScopePrefix(func->implementingClass(), value); + fieldName.prepend(prefix); + prefix.clear(); + } else { + fieldName.prepend(QLatin1String(CPP_SELF_VAR) + QLatin1String("->")); + } + value.replace(match.captured(1), fieldName); + break; + } + } + } + } + + if (!prefix.isEmpty()) + value.prepend(prefix); + return value; +} + +QString ShibokenGenerator::cpythonEnumName(const EnumTypeEntry *enumEntry) +{ + QString p = enumEntry->targetLangPackage(); + p.replace(QLatin1Char('.'), QLatin1Char('_')); + return cpythonEnumFlagsName(p, enumEntry->qualifiedCppName()); +} + +QString ShibokenGenerator::cpythonEnumName(const AbstractMetaEnum *metaEnum) +{ + return cpythonEnumName(metaEnum->typeEntry()); +} + +QString ShibokenGenerator::cpythonFlagsName(const FlagsTypeEntry *flagsEntry) +{ + QString p = flagsEntry->targetLangPackage(); + p.replace(QLatin1Char('.'), QLatin1Char('_')); + return cpythonEnumFlagsName(p, flagsEntry->originalName()); +} + +QString ShibokenGenerator::cpythonFlagsName(const AbstractMetaEnum *metaEnum) +{ + const FlagsTypeEntry *flags = metaEnum->typeEntry()->flags(); + if (!flags) + return QString(); + return cpythonFlagsName(flags); +} + +QString ShibokenGenerator::cpythonSpecialCastFunctionName(const AbstractMetaClass *metaClass) +{ + return cpythonBaseName(metaClass->typeEntry()) + QLatin1String("SpecialCastFunction"); +} + +QString ShibokenGenerator::cpythonWrapperCPtr(const AbstractMetaClass *metaClass, + const QString &argName) const +{ + return cpythonWrapperCPtr(metaClass->typeEntry(), argName); +} + +QString ShibokenGenerator::cpythonWrapperCPtr(const AbstractMetaType &metaType, + const QString &argName) const +{ + if (!ShibokenGenerator::isWrapperType(metaType.typeEntry())) + return QString(); + return QLatin1String("reinterpret_cast< ::") + metaType.cppSignature() + + QLatin1String(" *>(Shiboken::Conversions::cppPointer(") + cpythonTypeNameExt(metaType) + + QLatin1String(", reinterpret_cast<SbkObject *>(") + argName + QLatin1String(")))"); +} + +QString ShibokenGenerator::cpythonWrapperCPtr(const TypeEntry *type, + const QString &argName) const +{ + if (!ShibokenGenerator::isWrapperType(type)) + return QString(); + return QLatin1String("reinterpret_cast< ::") + type->qualifiedCppName() + + QLatin1String(" *>(Shiboken::Conversions::cppPointer(") + cpythonTypeNameExt(type) + + QLatin1String(", reinterpret_cast<SbkObject *>(") + argName + QLatin1String(")))"); +} + +void ShibokenGenerator::writeToPythonConversion(QTextStream & s, const AbstractMetaType &type, + const AbstractMetaClass * /* context */, + const QString &argumentName) +{ + s << cpythonToPythonConversionFunction(type) << argumentName << ')'; +} + +void ShibokenGenerator::writeToCppConversion(QTextStream &s, const AbstractMetaClass *metaClass, + const QString &inArgName, const QString &outArgName) +{ + s << cpythonToCppConversionFunction(metaClass) << inArgName << ", &" << outArgName << ')'; +} + +void ShibokenGenerator::writeToCppConversion(QTextStream &s, const AbstractMetaType &type, const AbstractMetaClass *context, + const QString &inArgName, const QString &outArgName) +{ + s << cpythonToCppConversionFunction(type, context) << inArgName << ", &" << outArgName << ')'; +} + +bool ShibokenGenerator::shouldRejectNullPointerArgument(const AbstractMetaFunction *func, int argIndex) +{ + if (argIndex < 0 || argIndex >= func->arguments().count()) + return false; + + const AbstractMetaArgument &arg = func->arguments().at(argIndex); + if (isValueTypeWithCopyConstructorOnly(arg.type())) + return true; + + // Argument type is not a pointer, a None rejection should not be + // necessary because the type checking would handle that already. + if (!isPointer(arg.type())) + return false; + if (func->argumentRemoved(argIndex + 1)) + return false; + const FunctionModificationList &mods = func->modifications(); + for (const FunctionModification &funcMod : mods) { + for (const ArgumentModification &argMod : funcMod.argument_mods) { + if (argMod.index == argIndex + 1 && argMod.noNullPointers) + return true; + } + } + return false; +} + +QString ShibokenGenerator::getFormatUnitString(const AbstractMetaFunction *func, bool incRef) const +{ + QString result; + const char objType = (incRef ? 'O' : 'N'); + const AbstractMetaArgumentList &arguments = func->arguments(); + for (const AbstractMetaArgument &arg : arguments) { + if (func->argumentRemoved(arg.argumentIndex() + 1)) + continue; + + const auto &type = arg.type(); + if (!func->typeReplaced(arg.argumentIndex() + 1).isEmpty()) { + result += QLatin1Char(objType); + } else if (arg.type().isObject() + || type.isValue() + || type.isValuePointer() + || type.isNativePointer() + || type.isEnum() + || type.isFlags() + || type.isContainer() + || type.isSmartPointer() + || type.referenceType() == LValueReference) { + result += QLatin1Char(objType); + } else if (type.isPrimitive()) { + const auto *ptype = + static_cast<const PrimitiveTypeEntry *>(type.typeEntry()); + if (ptype->basicReferencedTypeEntry()) + ptype = ptype->basicReferencedTypeEntry(); + if (m_formatUnits.contains(ptype->name())) + result += m_formatUnits[ptype->name()]; + else + result += QLatin1Char(objType); + } else if (isCString(type)) { + result += QLatin1Char('z'); + } else { + qCWarning(lcShiboken).noquote().nospace() + << "Method: " << func->ownerClass()->qualifiedCppName() + << "::" << func->signature() << " => Arg:" + << arg.name() << "index: " << arg.argumentIndex() + << " - cannot be handled properly. Use an inject-code to fix it!"; + result += QLatin1Char('?'); + } + } + return result; +} + +QString ShibokenGenerator::cpythonBaseName(const AbstractMetaType &type) +{ + if (isCString(type)) + return QLatin1String("PyString"); + return cpythonBaseName(type.typeEntry()); +} + +QString ShibokenGenerator::cpythonBaseName(const AbstractMetaClass *metaClass) +{ + return cpythonBaseName(metaClass->typeEntry()); +} + +QString ShibokenGenerator::cpythonBaseName(const TypeEntry *type) +{ + QString baseName; + if (ShibokenGenerator::isWrapperType(type) || type->isNamespace()) { // && type->referenceType() == NoReference) { + baseName = QLatin1String("Sbk_") + type->name(); + } else if (type->isPrimitive()) { + const auto *ptype = static_cast<const PrimitiveTypeEntry *>(type); + while (ptype->basicReferencedTypeEntry()) + ptype = ptype->basicReferencedTypeEntry(); + if (ptype->targetLangApiName() == ptype->name()) + baseName = pythonPrimitiveTypeName(ptype->name()); + else + baseName = ptype->targetLangApiName(); + } else if (type->isEnum()) { + baseName = cpythonEnumName(static_cast<const EnumTypeEntry *>(type)); + } else if (type->isFlags()) { + baseName = cpythonFlagsName(static_cast<const FlagsTypeEntry *>(type)); + } else if (type->isContainer()) { + const auto *ctype = static_cast<const ContainerTypeEntry *>(type); + switch (ctype->containerKind()) { + case ContainerTypeEntry::ListContainer: + case ContainerTypeEntry::StringListContainer: + case ContainerTypeEntry::LinkedListContainer: + case ContainerTypeEntry::VectorContainer: + case ContainerTypeEntry::StackContainer: + case ContainerTypeEntry::QueueContainer: + //baseName = "PyList"; + //break; + case ContainerTypeEntry::PairContainer: + //baseName = "PyTuple"; + baseName = QLatin1String("PySequence"); + break; + case ContainerTypeEntry::SetContainer: + baseName = QLatin1String("PySet"); + break; + case ContainerTypeEntry::MapContainer: + case ContainerTypeEntry::MultiMapContainer: + case ContainerTypeEntry::HashContainer: + case ContainerTypeEntry::MultiHashContainer: + baseName = QLatin1String("PyDict"); + break; + default: + Q_ASSERT(false); + } + } else { + baseName = QLatin1String("PyObject"); + } + return baseName.replace(QLatin1String("::"), QLatin1String("_")); +} + +QString ShibokenGenerator::cpythonTypeName(const AbstractMetaClass *metaClass) +{ + return cpythonTypeName(metaClass->typeEntry()); +} + +QString ShibokenGenerator::cpythonTypeName(const TypeEntry *type) +{ + return cpythonBaseName(type) + QLatin1String("_TypeF()"); +} + +QString ShibokenGenerator::cpythonTypeNameExt(const TypeEntry *type) const +{ + return cppApiVariableName(type->targetLangPackage()) + QLatin1Char('[') + + getTypeIndexVariableName(type) + QLatin1Char(']'); +} + +QString ShibokenGenerator::converterObject(const AbstractMetaType &type) +{ + if (isCString(type)) + return QLatin1String("Shiboken::Conversions::PrimitiveTypeConverter<const char *>()"); + if (isVoidPointer(type)) + return QLatin1String("Shiboken::Conversions::PrimitiveTypeConverter<void *>()"); + const AbstractMetaTypeList nestedArrayTypes = type.nestedArrayTypes(); + if (!nestedArrayTypes.isEmpty() && nestedArrayTypes.constLast().isCppPrimitive()) { + return QStringLiteral("Shiboken::Conversions::ArrayTypeConverter<") + + nestedArrayTypes.constLast().minimalSignature() + + QLatin1String(">(") + QString::number(nestedArrayTypes.size()) + + QLatin1Char(')'); + } + + auto typeEntry = type.typeEntry(); + if (typeEntry->isContainer() || typeEntry->isSmartPointer()) { + return convertersVariableName(typeEntry->targetLangPackage()) + + QLatin1Char('[') + getTypeIndexVariableName(type) + QLatin1Char(']'); + } + return converterObject(typeEntry); +} + +QString ShibokenGenerator::converterObject(const TypeEntry *type) +{ + if (isCppPrimitive(type)) + return QString::fromLatin1("Shiboken::Conversions::PrimitiveTypeConverter<%1>()").arg(type->qualifiedCppName()); + if (isWrapperType(type) || type->isEnum() || type->isFlags()) + return QString::fromLatin1("*PepType_SGTP(%1)->converter").arg(cpythonTypeNameExt(type)); + + if (type->isArray()) { + qDebug() << "Warning: no idea how to handle the Qt5 type " << type->qualifiedCppName(); + return QString(); + } + + /* the typedef'd primitive types case */ + const auto *pte = dynamic_cast<const PrimitiveTypeEntry *>(type); + if (!pte) { + qDebug() << "Warning: the Qt5 primitive type is unknown" << type->qualifiedCppName(); + return QString(); + } + if (pte->basicReferencedTypeEntry()) + pte = pte->basicReferencedTypeEntry(); + if (pte->isPrimitive() && !pte->isCppPrimitive() && !pte->customConversion()) + return QString::fromLatin1("Shiboken::Conversions::PrimitiveTypeConverter<%1>()").arg(pte->qualifiedCppName()); + + return convertersVariableName(type->targetLangPackage()) + + QLatin1Char('[') + getTypeIndexVariableName(type) + QLatin1Char(']'); +} + +QString ShibokenGenerator::cpythonTypeNameExt(const AbstractMetaType &type) const +{ + return cppApiVariableName(type.typeEntry()->targetLangPackage()) + QLatin1Char('[') + + getTypeIndexVariableName(type) + QLatin1Char(']'); +} + +static inline QString unknownOperator() { return QStringLiteral("__UNKNOWN_OPERATOR__"); } + +QString ShibokenGenerator::fixedCppTypeName(const CustomConversion::TargetToNativeConversion *toNative) +{ + if (toNative->sourceType()) + return fixedCppTypeName(toNative->sourceType()); + return toNative->sourceTypeName(); +} +QString ShibokenGenerator::fixedCppTypeName(const AbstractMetaType &type) +{ + return fixedCppTypeName(type.typeEntry(), type.cppSignature()); +} + +static QString _fixedCppTypeName(QString typeName) +{ + typeName.remove(QLatin1Char(' ')); + typeName.replace(QLatin1Char('.'), QLatin1Char('_')); + typeName.replace(QLatin1Char(','), QLatin1Char('_')); + typeName.replace(QLatin1Char('<'), QLatin1Char('_')); + typeName.replace(QLatin1Char('>'), QLatin1Char('_')); + typeName.replace(QLatin1String("::"), QLatin1String("_")); + typeName.replace(QLatin1String("*"), QLatin1String("PTR")); + typeName.replace(QLatin1String("&"), QLatin1String("REF")); + return typeName; +} +QString ShibokenGenerator::fixedCppTypeName(const TypeEntry *type, QString typeName) +{ + if (typeName.isEmpty()) + typeName = type->qualifiedCppName(); + if (!type->generateCode()) { + typeName.prepend(QLatin1Char('_')); + typeName.prepend(type->targetLangPackage()); + } + return _fixedCppTypeName(typeName); +} + +QString ShibokenGenerator::pythonPrimitiveTypeName(const QString &cppTypeName) +{ + QString rv = ShibokenGenerator::m_pythonPrimitiveTypeName.value(cppTypeName, QString()); + if (rv.isEmpty()) { + // activate this when some primitive types are missing, + // i.e. when shiboken itself fails to build. + // In general, this is valid while just called by isNumeric() + // used on Qt5, 2015-09-20 + if (false) { + std::cerr << "primitive type not found: " << qPrintable(cppTypeName) << std::endl; + abort(); + } + } + return rv; +} + +QString ShibokenGenerator::pythonPrimitiveTypeName(const PrimitiveTypeEntry *type) +{ + while (type->basicReferencedTypeEntry()) + type = type->basicReferencedTypeEntry(); + return pythonPrimitiveTypeName(type->name()); +} + +QString ShibokenGenerator::pythonOperatorFunctionName(const QString &cppOpFuncName) +{ + QString value = m_pythonOperators.value(cppOpFuncName); + if (value.isEmpty()) + return unknownOperator(); + value.prepend(QLatin1String("__")); + value.append(QLatin1String("__")); + return value; +} + +QString ShibokenGenerator::pythonOperatorFunctionName(const AbstractMetaFunction *func) +{ + QString op = pythonOperatorFunctionName(func->originalName()); + if (op == unknownOperator()) + qCWarning(lcShiboken).noquote().nospace() << msgUnknownOperator(func); + if (func->arguments().isEmpty()) { + if (op == QLatin1String("__sub__")) + op = QLatin1String("__neg__"); + else if (op == QLatin1String("__add__")) + op = QLatin1String("__pos__"); + } else if (func->isStatic() && func->arguments().size() == 2) { + // If a operator overload function has 2 arguments and + // is static we assume that it is a reverse operator. + op = op.insert(2, QLatin1Char('r')); + } + return op; +} + +QString ShibokenGenerator::pythonRichCompareOperatorId(const QString &cppOpFuncName) +{ + return QLatin1String("Py_") + m_pythonOperators.value(cppOpFuncName).toUpper(); +} + +QString ShibokenGenerator::pythonRichCompareOperatorId(const AbstractMetaFunction *func) +{ + return pythonRichCompareOperatorId(func->originalName()); +} + +bool ShibokenGenerator::isNumber(const QString &cpythonApiName) +{ + return cpythonApiName == QLatin1String("PyInt") + || cpythonApiName == QLatin1String("PyFloat") + || cpythonApiName == QLatin1String("PyLong") + || cpythonApiName == QLatin1String("PyBool"); +} + +bool ShibokenGenerator::isNumber(const TypeEntry *type) +{ + if (!type->isPrimitive()) + return false; + return isNumber(pythonPrimitiveTypeName(static_cast<const PrimitiveTypeEntry *>(type))); +} + +bool ShibokenGenerator::isNumber(const AbstractMetaType &type) +{ + return isNumber(type.typeEntry()); +} + +bool ShibokenGenerator::isPyInt(const TypeEntry *type) +{ + if (!type->isPrimitive()) + return false; + return pythonPrimitiveTypeName(static_cast<const PrimitiveTypeEntry *>(type)) + == QLatin1String("PyInt"); +} + +bool ShibokenGenerator::isPyInt(const AbstractMetaType &type) +{ + return isPyInt(type.typeEntry()); +} + +bool ShibokenGenerator::isWrapperType(const TypeEntry *type) +{ + if (type->isComplex()) + return ShibokenGenerator::isWrapperType(static_cast<const ComplexTypeEntry *>(type)); + return type->isObject() || type->isValue() || type->isSmartPointer(); +} +bool ShibokenGenerator::isWrapperType(const ComplexTypeEntry *type) +{ + return isObjectType(type) || type->isValue() || type->isSmartPointer(); +} +bool ShibokenGenerator::isWrapperType(const AbstractMetaType &metaType) +{ + return isObjectType(metaType) + || metaType.typeEntry()->isValue() + || metaType.typeEntry()->isSmartPointer(); +} + +bool ShibokenGenerator::isPointerToWrapperType(const AbstractMetaType &type) +{ + return (isObjectType(type) && type.indirections() == 1) || type.isValuePointer(); +} + +bool ShibokenGenerator::isObjectTypeUsedAsValueType(const AbstractMetaType &type) +{ + return type.typeEntry()->isObject() && type.referenceType() == NoReference && type.indirections() == 0; +} + +bool ShibokenGenerator::isValueTypeWithCopyConstructorOnly(const AbstractMetaClass *metaClass) +{ + if (!metaClass || !metaClass->typeEntry()->isValue()) + return false; + if (metaClass->attributes().testFlag(AbstractMetaAttributes::HasRejectedDefaultConstructor)) + return false; + const AbstractMetaFunctionList ctors = + metaClass->queryFunctions(AbstractMetaClass::Constructors); + bool copyConstructorFound = false; + for (auto ctor : ctors) { + switch (ctor->functionType()) { + case AbstractMetaFunction::ConstructorFunction: + return false; + case AbstractMetaFunction::CopyConstructorFunction: + copyConstructorFound = true; + break; + case AbstractMetaFunction::MoveConstructorFunction: + break; + default: + Q_ASSERT(false); + break; + } + } + return copyConstructorFound; +} + +bool ShibokenGenerator::isValueTypeWithCopyConstructorOnly(const TypeEntry *type) const +{ + if (!type || !type->isValue()) + return false; + return isValueTypeWithCopyConstructorOnly(AbstractMetaClass::findClass(classes(), type)); +} + +bool ShibokenGenerator::isValueTypeWithCopyConstructorOnly(const AbstractMetaType &type) const +{ + return type.typeEntry()->isValue() + && isValueTypeWithCopyConstructorOnly(type.typeEntry()); +} + +bool ShibokenGenerator::isUserPrimitive(const TypeEntry *type) +{ + if (!type->isPrimitive()) + return false; + const auto *trueType = static_cast<const PrimitiveTypeEntry *>(type); + if (trueType->basicReferencedTypeEntry()) + trueType = trueType->basicReferencedTypeEntry(); + return trueType->isPrimitive() && !trueType->isCppPrimitive() + && trueType->qualifiedCppName() != QLatin1String("std::string"); +} + +bool ShibokenGenerator::isUserPrimitive(const AbstractMetaType &type) +{ + if (type.indirections() != 0) + return false; + return isUserPrimitive(type.typeEntry()); +} + +bool ShibokenGenerator::isCppPrimitive(const TypeEntry *type) +{ + if (type->isCppPrimitive()) + return true; + if (!type->isPrimitive()) + return false; + const auto *trueType = static_cast<const PrimitiveTypeEntry *>(type); + if (trueType->basicReferencedTypeEntry()) + trueType = trueType->basicReferencedTypeEntry(); + return trueType->qualifiedCppName() == QLatin1String("std::string"); +} + +bool ShibokenGenerator::isCppPrimitive(const AbstractMetaType &type) +{ + if (isCString(type) || isVoidPointer(type)) + return true; + if (type.indirections() != 0) + return false; + return isCppPrimitive(type.typeEntry()); +} + +bool ShibokenGenerator::shouldDereferenceArgumentPointer(const AbstractMetaArgument &arg) +{ + return shouldDereferenceAbstractMetaTypePointer(arg.type()); +} + +bool ShibokenGenerator::shouldDereferenceAbstractMetaTypePointer(const AbstractMetaType &metaType) +{ + return metaType.referenceType() == LValueReference && isWrapperType(metaType) && !isPointer(metaType); +} + +bool ShibokenGenerator::visibilityModifiedToPrivate(const AbstractMetaFunction *func) +{ + const FunctionModificationList &mods = func->modifications(); + for (const FunctionModification &mod : mods) { + if (mod.modifiers & Modification::Private) + return true; + } + return false; +} + +bool ShibokenGenerator::isNullPtr(const QString &value) +{ + return value == QLatin1String("0") || value == QLatin1String("nullptr") + || value == QLatin1String("NULLPTR") || value == QLatin1String("{}"); +} + +QString ShibokenGenerator::cpythonCheckFunction(AbstractMetaType metaType, bool genericNumberType) +{ + QString customCheck; + if (metaType.typeEntry()->isCustom()) { + AbstractMetaType type; + customCheck = guessCPythonCheckFunction(metaType.typeEntry()->name(), &type); + if (type) + metaType = type; + if (!customCheck.isEmpty()) + return customCheck; + } + + if (isCppPrimitive(metaType)) { + if (isCString(metaType)) + return QLatin1String("Shiboken::String::check"); + if (isVoidPointer(metaType)) + return QLatin1String("PyObject_Check"); + return cpythonCheckFunction(metaType.typeEntry(), genericNumberType); + } + auto typeEntry = metaType.typeEntry(); + if (typeEntry->isContainer()) { + QString typeCheck = QLatin1String("Shiboken::Conversions::"); + ContainerTypeEntry::ContainerKind type = + static_cast<const ContainerTypeEntry *>(typeEntry)->containerKind(); + if (type == ContainerTypeEntry::ListContainer + || type == ContainerTypeEntry::StringListContainer + || type == ContainerTypeEntry::LinkedListContainer + || type == ContainerTypeEntry::VectorContainer + || type == ContainerTypeEntry::StackContainer + || type == ContainerTypeEntry::SetContainer + || type == ContainerTypeEntry::QueueContainer) { + const AbstractMetaType &type = metaType.instantiations().constFirst(); + if (isPointerToWrapperType(type)) { + typeCheck += QString::fromLatin1("checkSequenceTypes(%1, ").arg(cpythonTypeNameExt(type)); + } else if (isWrapperType(type)) { + typeCheck += QLatin1String("convertibleSequenceTypes(reinterpret_cast<SbkObjectType *>("); + typeCheck += cpythonTypeNameExt(type); + typeCheck += QLatin1String("), "); + } else { + typeCheck += QString::fromLatin1("convertibleSequenceTypes(%1, ").arg(converterObject(type)); + } + } else if (type == ContainerTypeEntry::MapContainer + || type == ContainerTypeEntry::MultiMapContainer + || type == ContainerTypeEntry::HashContainer + || type == ContainerTypeEntry::MultiHashContainer + || type == ContainerTypeEntry::PairContainer) { + QString pyType = (type == ContainerTypeEntry::PairContainer) ? QLatin1String("Pair") : QLatin1String("Dict"); + const AbstractMetaType &firstType = metaType.instantiations().constFirst(); + const AbstractMetaType &secondType = metaType.instantiations().constLast(); + if (isPointerToWrapperType(firstType) && isPointerToWrapperType(secondType)) { + typeCheck += QString::fromLatin1("check%1Types(%2, %3, ") + .arg(pyType, cpythonTypeNameExt(firstType), cpythonTypeNameExt(secondType)); + } else { + typeCheck += QString::fromLatin1("convertible%1Types(%2, %3, %4, %5, ") + .arg(pyType, converterObject(firstType), + isPointerToWrapperType(firstType) ? QLatin1String("true") : QLatin1String("false"), + converterObject(secondType), + isPointerToWrapperType(secondType) ? QLatin1String("true") : QLatin1String("false")); + } + } + return typeCheck; + } + return cpythonCheckFunction(typeEntry, genericNumberType); +} + +QString ShibokenGenerator::cpythonCheckFunction(const TypeEntry *type, bool genericNumberType) +{ + QString customCheck; + if (type->isCustom()) { + AbstractMetaType metaType; + customCheck = guessCPythonCheckFunction(type->name(), &metaType); + if (metaType) { + const QString result = cpythonCheckFunction(metaType, genericNumberType); + return result; + } + return customCheck; + } + + if (type->isEnum() || type->isFlags() || isWrapperType(type)) + return QString::fromLatin1("SbkObject_TypeCheck(%1, ").arg(cpythonTypeNameExt(type)); + if (isCppPrimitive(type)) { + return pythonPrimitiveTypeName(static_cast<const PrimitiveTypeEntry *>(type)) + + QLatin1String("_Check"); + } + QString typeCheck; + if (type->targetLangApiName() == type->name()) + typeCheck = cpythonIsConvertibleFunction(type); + else if (type->targetLangApiName() == QLatin1String("PyUnicode")) + typeCheck = QLatin1String("Shiboken::String::check"); + else + typeCheck = type->targetLangApiName() + QLatin1String("_Check"); + return typeCheck; +} + +QString ShibokenGenerator::guessCPythonCheckFunction(const QString &type, AbstractMetaType *metaType) +{ + *metaType = {}; + // PYSIDE-795: We abuse PySequence for iterables. + // This part handles the overrides in the XML files. + if (type == QLatin1String("PySequence")) + return QLatin1String("Shiboken::String::checkIterable"); + + if (type == QLatin1String("PyTypeObject")) + return QLatin1String("PyType_Check"); + + if (type == QLatin1String("PyBuffer")) + return QLatin1String("Shiboken::Buffer::checkType"); + + if (type == QLatin1String("str")) + return QLatin1String("Shiboken::String::check"); + + *metaType = buildAbstractMetaTypeFromString(type); + if (*metaType && !metaType->typeEntry()->isCustom()) + return QString(); + + return type + QLatin1String("_Check"); +} + +QString ShibokenGenerator::cpythonIsConvertibleFunction(const TypeEntry *type, + bool /* genericNumberType */, + bool /* checkExact */) +{ + if (isWrapperType(type)) { + QString result = QLatin1String("Shiboken::Conversions::"); + result += (type->isValue() && !isValueTypeWithCopyConstructorOnly(type)) + ? QLatin1String("isPythonToCppValueConvertible") + : QLatin1String("isPythonToCppPointerConvertible"); + result += QLatin1String("(reinterpret_cast<SbkObjectType *>(") + + cpythonTypeNameExt(type) + QLatin1String("), "); + return result; + } + return QString::fromLatin1("Shiboken::Conversions::isPythonToCppConvertible(%1, ") + .arg(converterObject(type)); +} +QString ShibokenGenerator::cpythonIsConvertibleFunction(AbstractMetaType metaType, + bool /* genericNumberType */) +{ + QString customCheck; + if (metaType.typeEntry()->isCustom()) { + AbstractMetaType type; + customCheck = guessCPythonCheckFunction(metaType.typeEntry()->name(), &type); + if (type) + metaType = type; + if (!customCheck.isEmpty()) + return customCheck; + } + + QString result = QLatin1String("Shiboken::Conversions::"); + if (isWrapperType(metaType)) { + if (isPointer(metaType) || isValueTypeWithCopyConstructorOnly(metaType)) + result += QLatin1String("isPythonToCppPointerConvertible"); + else if (metaType.referenceType() == LValueReference) + result += QLatin1String("isPythonToCppReferenceConvertible"); + else + result += QLatin1String("isPythonToCppValueConvertible"); + result += QLatin1String("(reinterpret_cast<SbkObjectType *>(") + + cpythonTypeNameExt(metaType) + QLatin1String("), "); + return result; + } + result += QLatin1String("isPythonToCppConvertible(") + converterObject(metaType); + // Write out array sizes if known + const AbstractMetaTypeList nestedArrayTypes = metaType.nestedArrayTypes(); + if (!nestedArrayTypes.isEmpty() && nestedArrayTypes.constLast().isCppPrimitive()) { + const int dim1 = metaType.arrayElementCount(); + const int dim2 = nestedArrayTypes.constFirst().isArray() + ? nestedArrayTypes.constFirst().arrayElementCount() : -1; + result += QLatin1String(", ") + QString::number(dim1) + + QLatin1String(", ") + QString::number(dim2); + } + result += QLatin1String(", "); + return result; +} + +QString ShibokenGenerator::cpythonIsConvertibleFunction(const AbstractMetaArgument &metaArg, bool genericNumberType) +{ + return cpythonIsConvertibleFunction(metaArg.type(), genericNumberType); +} + +QString ShibokenGenerator::cpythonToCppConversionFunction(const AbstractMetaClass *metaClass) +{ + return QLatin1String("Shiboken::Conversions::pythonToCppPointer(reinterpret_cast<SbkObjectType *>(") + + cpythonTypeNameExt(metaClass->typeEntry()) + QLatin1String("), "); +} + +QString ShibokenGenerator::cpythonToCppConversionFunction(const AbstractMetaType &type, + const AbstractMetaClass * /* context */) +{ + if (isWrapperType(type)) { + return QLatin1String("Shiboken::Conversions::pythonToCpp") + + (isPointer(type) ? QLatin1String("Pointer") : QLatin1String("Copy")) + + QLatin1String("(reinterpret_cast<SbkObjectType *>(") + + cpythonTypeNameExt(type) + QLatin1String("), "); + } + return QStringLiteral("Shiboken::Conversions::pythonToCppCopy(%1, ") + .arg(converterObject(type)); +} + +QString ShibokenGenerator::cpythonToPythonConversionFunction(const AbstractMetaType &type, + const AbstractMetaClass * /* context */) +{ + if (isWrapperType(type)) { + QString conversion; + if (type.referenceType() == LValueReference && !(type.isValue() && type.isConstant()) && !isPointer(type)) + conversion = QLatin1String("reference"); + else if (type.isValue() || type.isSmartPointer()) + conversion = QLatin1String("copy"); + else + conversion = QLatin1String("pointer"); + QString result = QLatin1String("Shiboken::Conversions::") + conversion + + QLatin1String("ToPython(reinterpret_cast<SbkObjectType *>(") + + cpythonTypeNameExt(type) + QLatin1String("), "); + if (conversion != QLatin1String("pointer")) + result += QLatin1Char('&'); + return result; + } + return QStringLiteral("Shiboken::Conversions::copyToPython(%1, %2") + .arg(converterObject(type), + (isCString(type) || isVoidPointer(type)) ? QString() : QLatin1String("&")); +} + +QString ShibokenGenerator::cpythonToPythonConversionFunction(const AbstractMetaClass *metaClass) +{ + return cpythonToPythonConversionFunction(metaClass->typeEntry()); +} + +QString ShibokenGenerator::cpythonToPythonConversionFunction(const TypeEntry *type) +{ + if (isWrapperType(type)) { + const QString conversion = type->isValue() ? QLatin1String("copy") : QLatin1String("pointer"); + QString result = QLatin1String("Shiboken::Conversions::") + conversion + + QLatin1String("ToPython(reinterpret_cast<SbkObjectType *>(") + cpythonTypeNameExt(type) + + QLatin1String("), "); + if (conversion != QLatin1String("pointer")) + result += QLatin1Char('&'); + return result; + } + + return QStringLiteral("Shiboken::Conversions::copyToPython(%1, &").arg(converterObject(type)); +} + +QString ShibokenGenerator::argumentString(const AbstractMetaFunction *func, + const AbstractMetaArgument &argument, + Options options) const +{ + QString modified_type; + if (!(options & OriginalTypeDescription)) + modified_type = func->typeReplaced(argument.argumentIndex() + 1); + QString arg; + + if (modified_type.isEmpty()) + arg = translateType(argument.type(), func->implementingClass(), options); + else + arg = modified_type.replace(QLatin1Char('$'), QLatin1Char('.')); + + if (!(options & Generator::SkipName)) { + // "int a", "int a[]" + const int arrayPos = arg.indexOf(QLatin1Char('[')); + if (arrayPos != -1) + arg.insert(arrayPos, QLatin1Char(' ') + argument.name()); + else + arg.append(QLatin1Char(' ') + argument.name()); + } + + if ((options & Generator::SkipDefaultValues) != Generator::SkipDefaultValues && + !argument.originalDefaultValueExpression().isEmpty()) + { + QString default_value = argument.originalDefaultValueExpression(); + if (default_value == QLatin1String("NULL")) + default_value = QLatin1String(NULL_PTR); + + //WORKAROUND: fix this please + if (default_value.startsWith(QLatin1String("new "))) + default_value.remove(0, 4); + + arg += QLatin1String(" = ") + default_value; + } + + return arg; +} + +void ShibokenGenerator::writeArgument(QTextStream &s, + const AbstractMetaFunction *func, + const AbstractMetaArgument &argument, + Options options) const +{ + s << argumentString(func, argument, options); +} + +void ShibokenGenerator::writeFunctionArguments(QTextStream &s, + const AbstractMetaFunction *func, + Options options) const +{ + AbstractMetaArgumentList arguments = func->arguments(); + + if (options & Generator::WriteSelf) { + s << func->implementingClass()->name() << '&'; + if (!(options & SkipName)) + s << " self"; + } + + int argUsed = 0; + for (int i = 0; i < arguments.size(); ++i) { + if ((options & Generator::SkipRemovedArguments) && func->argumentRemoved(i+1)) + continue; + + if ((options & Generator::WriteSelf) || argUsed != 0) + s << ", "; + writeArgument(s, func, arguments[i], options); + argUsed++; + } +} + +GeneratorContext ShibokenGenerator::contextForClass(const AbstractMetaClass *c) const +{ + GeneratorContext result = Generator::contextForClass(c); + if (shouldGenerateCppWrapper(c)) { + result.m_type = GeneratorContext::WrappedClass; + result.m_wrappername = wrapperName(c); + } + return result; +} + +QString ShibokenGenerator::functionReturnType(const AbstractMetaFunction *func, Options options) const +{ + QString modifiedReturnType = QString(func->typeReplaced(0)); + if (!modifiedReturnType.isEmpty() && !(options & OriginalTypeDescription)) + return modifiedReturnType; + return translateType(func->type(), func->implementingClass(), options); +} + +QString ShibokenGenerator::functionSignature(const AbstractMetaFunction *func, + const QString &prepend, + const QString &append, + Options options, + int /* argCount */) const +{ + QString result; + QTextStream s(&result); + // The actual function + if (!(func->isEmptyFunction() || + func->isNormal() || + func->isSignal())) { + options |= Generator::SkipReturnType; + } else { + s << functionReturnType(func, options) << ' '; + } + + // name + QString name(func->originalName()); + if (func->isConstructor()) + name = wrapperName(func->ownerClass()); + + s << prepend << name << append << '('; + writeFunctionArguments(s, func, options); + s << ')'; + + if (func->isConstant() && !(options & Generator::ExcludeMethodConst)) + s << " const"; + + if (func->exceptionSpecification() == ExceptionSpecification::NoExcept) + s << " noexcept"; + + return result; +} + +void ShibokenGenerator::writeArgumentNames(QTextStream &s, + const AbstractMetaFunction *func, + Options options) const +{ + const AbstractMetaArgumentList arguments = func->arguments(); + int argCount = 0; + for (auto argument : arguments) { + const int index = argument.argumentIndex() + 1; + if ((options & Generator::SkipRemovedArguments) && (func->argumentRemoved(index))) + continue; + + s << ((argCount > 0) ? ", " : "") << argument.name(); + + if (((options & Generator::VirtualCall) == 0) + && (!func->conversionRule(TypeSystem::NativeCode, index).isEmpty() + || !func->conversionRule(TypeSystem::TargetLangCode, index).isEmpty()) + && !func->isConstructor()) { + s << CONV_RULE_OUT_VAR_SUFFIX; + } + + argCount++; + } +} + +void ShibokenGenerator::writeFunctionCall(QTextStream &s, + const AbstractMetaFunction *func, + Options options) const +{ + if (!(options & Generator::SkipName)) + s << (func->isConstructor() ? func->ownerClass()->qualifiedCppName() : func->originalName()); + s << '('; + writeArgumentNames(s, func, options); + s << ')'; +} + +void ShibokenGenerator::writeUnusedVariableCast(QTextStream &s, const QString &variableName) +{ + s << INDENT << "SBK_UNUSED(" << variableName<< ")\n"; +} + +static bool filterFunction(const AbstractMetaFunction *func, bool avoidProtectedHack) +{ + switch (func->functionType()) { + case AbstractMetaFunction::DestructorFunction: + case AbstractMetaFunction::SignalFunction: + case AbstractMetaFunction::GetAttroFunction: + case AbstractMetaFunction::SetAttroFunction: + return false; + default: + break; + } + if (func->usesRValueReferences()) + return false; + if (func->isModifiedRemoved() && !func->isAbstract() + && (!avoidProtectedHack || !func->isProtected())) { + return false; + } + return true; +} + +AbstractMetaFunctionList ShibokenGenerator::filterFunctions(const AbstractMetaClass *metaClass) +{ + AbstractMetaFunctionList result; + const AbstractMetaFunctionList &funcs = metaClass->functions(); + result.reserve(funcs.size()); + for (AbstractMetaFunction *func : funcs) { + if (filterFunction(func, avoidProtectedHack())) + result.append(func); + } + return result; +} + +ShibokenGenerator::ExtendedConverterData ShibokenGenerator::getExtendedConverters() const +{ + ExtendedConverterData extConvs; + for (const AbstractMetaClass *metaClass : classes()) { + // Use only the classes for the current module. + if (!shouldGenerate(metaClass)) + continue; + const AbstractMetaFunctionList &overloads = metaClass->operatorOverloads(AbstractMetaClass::ConversionOp); + for (AbstractMetaFunction *convOp : overloads) { + // Get only the conversion operators that return a type from another module, + // that are value-types and were not removed in the type system. + const TypeEntry *convType = convOp->type().typeEntry(); + if (convType->generateCode() || !convType->isValue() + || convOp->isModifiedRemoved()) + continue; + extConvs[convType].append(convOp->ownerClass()); + } + } + return extConvs; +} + +QVector<const CustomConversion *> ShibokenGenerator::getPrimitiveCustomConversions() +{ + QVector<const CustomConversion *> conversions; + const PrimitiveTypeEntryList &primitiveTypeList = primitiveTypes(); + for (const PrimitiveTypeEntry *type : primitiveTypeList) { + if (!shouldGenerateTypeEntry(type) || !isUserPrimitive(type) || !type->customConversion()) + continue; + + conversions << type->customConversion(); + } + return conversions; +} + +static QString getArgumentsFromMethodCall(const QString &str) +{ + // It would be way nicer to be able to use a Perl like + // regular expression that accepts temporary variables + // to count the parenthesis. + // For more information check this: + // http://perl.plover.com/yak/regex/samples/slide083.html + static QLatin1String funcCall("%CPPSELF.%FUNCTION_NAME"); + int pos = str.indexOf(funcCall); + if (pos == -1) + return QString(); + pos = pos + funcCall.size(); + while (str.at(pos) == QLatin1Char(' ') || str.at(pos) == QLatin1Char('\t')) + ++pos; + if (str.at(pos) == QLatin1Char('(')) + ++pos; + int begin = pos; + int counter = 1; + while (counter != 0) { + if (str.at(pos) == QLatin1Char('(')) + ++counter; + else if (str.at(pos) == QLatin1Char(')')) + --counter; + ++pos; + } + return str.mid(begin, pos-begin-1); +} + +QString ShibokenGenerator::getCodeSnippets(const CodeSnipList &codeSnips, + TypeSystem::CodeSnipPosition position, + TypeSystem::Language language) +{ + QString code; + QTextStream c(&code); + for (const CodeSnip &snip : codeSnips) { + if ((position != TypeSystem::CodeSnipPositionAny && snip.position != position) || !(snip.language & language)) + continue; + QString snipCode; + QTextStream sc(&snipCode); + formatCode(sc, snip.code(), INDENT); + c << snipCode; + } + return code; +} + +void ShibokenGenerator::processClassCodeSnip(QString &code, const GeneratorContext &context) +{ + auto metaClass = context.metaClass(); + // Replace template variable by the Python Type object + // for the class context in which the variable is used. + code.replace(QLatin1String("%PYTHONTYPEOBJECT"), + cpythonTypeName(metaClass) + QLatin1String("->type")); + const QString className = context.useWrapper() + ? context.wrapperName() : metaClass->qualifiedCppName(); + code.replace(QLatin1String("%TYPE"), className); + code.replace(QLatin1String("%CPPTYPE"), metaClass->name()); + + processCodeSnip(code); +} + +void ShibokenGenerator::processCodeSnip(QString &code) +{ + // replace "toPython" converters + replaceConvertToPythonTypeSystemVariable(code); + + // replace "toCpp" converters + replaceConvertToCppTypeSystemVariable(code); + + // replace "isConvertible" check + replaceIsConvertibleToCppTypeSystemVariable(code); + + // replace "checkType" check + replaceTypeCheckTypeSystemVariable(code); +} + +ShibokenGenerator::ArgumentVarReplacementList ShibokenGenerator::getArgumentReplacement(const AbstractMetaFunction *func, + bool usePyArgs, TypeSystem::Language language, + const AbstractMetaArgument *lastArg) +{ + ArgumentVarReplacementList argReplacements; + TypeSystem::Language convLang = (language == TypeSystem::TargetLangCode) + ? TypeSystem::NativeCode : TypeSystem::TargetLangCode; + int removed = 0; + for (int i = 0; i < func->arguments().size(); ++i) { + const AbstractMetaArgument &arg = func->arguments().at(i); + QString argValue; + if (language == TypeSystem::TargetLangCode) { + bool hasConversionRule = !func->conversionRule(convLang, i+1).isEmpty(); + const bool argRemoved = func->argumentRemoved(i+1); + if (argRemoved) + ++removed; + if (argRemoved && hasConversionRule) + argValue = arg.name() + QLatin1String(CONV_RULE_OUT_VAR_SUFFIX); + else if (argRemoved || (lastArg && arg.argumentIndex() > lastArg->argumentIndex())) + argValue = QLatin1String(CPP_ARG_REMOVED) + QString::number(i); + if (!argRemoved && argValue.isEmpty()) { + int argPos = i - removed; + AbstractMetaType type = arg.type(); + QString typeReplaced = func->typeReplaced(arg.argumentIndex() + 1); + if (!typeReplaced.isEmpty()) { + AbstractMetaType builtType = buildAbstractMetaTypeFromString(typeReplaced); + if (builtType) + type = builtType; + } + if (type.typeEntry()->isCustom()) { + argValue = usePyArgs + ? pythonArgsAt(argPos) : QLatin1String(PYTHON_ARG); + } else { + argValue = hasConversionRule + ? arg.name() + QLatin1String(CONV_RULE_OUT_VAR_SUFFIX) + : QLatin1String(CPP_ARG) + QString::number(argPos); + if (isWrapperType(type)) { + if (type.referenceType() == LValueReference && !isPointer(type)) + argValue.prepend(QLatin1Char('*')); + } + } + } + } else { + argValue = arg.name(); + } + if (!argValue.isEmpty()) + argReplacements << ArgumentVarReplacementPair(arg, argValue); + + } + return argReplacements; +} + +void ShibokenGenerator::writeClassCodeSnips(QTextStream &s, + const CodeSnipList &codeSnips, + TypeSystem::CodeSnipPosition position, + TypeSystem::Language language, + const GeneratorContext &context) +{ + QString code = getCodeSnippets(codeSnips, position, language); + if (code.isEmpty()) + return; + processClassCodeSnip(code, context); + s << INDENT << "// Begin code injection\n"; + s << code; + s << INDENT << "// End of code injection\n\n"; +} + +void ShibokenGenerator::writeCodeSnips(QTextStream &s, + const CodeSnipList &codeSnips, + TypeSystem::CodeSnipPosition position, + TypeSystem::Language language) +{ + QString code = getCodeSnippets(codeSnips, position, language); + if (code.isEmpty()) + return; + processCodeSnip(code); + s << INDENT << "// Begin code injection\n"; + s << code; + s << INDENT << "// End of code injection\n\n"; +} + +void ShibokenGenerator::writeCodeSnips(QTextStream &s, + const CodeSnipList &codeSnips, + TypeSystem::CodeSnipPosition position, + TypeSystem::Language language, + const AbstractMetaFunction *func, + const AbstractMetaArgument *lastArg) +{ + QString code = getCodeSnippets(codeSnips, position, language); + if (code.isEmpty()) + return; + + // Calculate the real number of arguments. + int argsRemoved = 0; + for (int i = 0; i < func->arguments().size(); i++) { + if (func->argumentRemoved(i+1)) + argsRemoved++; + } + + const auto &groups = func->implementingClass() + ? getFunctionGroups(func->implementingClass()) + : getGlobalFunctionGroups(); + OverloadData od(groups[func->name()], this); + bool usePyArgs = pythonFunctionWrapperUsesListOfArguments(od); + + // Replace %PYARG_# variables. + code.replace(QLatin1String("%PYARG_0"), QLatin1String(PYTHON_RETURN_VAR)); + + static const QRegularExpression pyArgsRegex(QStringLiteral("%PYARG_(\\d+)")); + Q_ASSERT(pyArgsRegex.isValid()); + if (language == TypeSystem::TargetLangCode) { + if (usePyArgs) { + code.replace(pyArgsRegex, QLatin1String(PYTHON_ARGS) + QLatin1String("[\\1-1]")); + } else { + static const QRegularExpression pyArgsRegexCheck(QStringLiteral("%PYARG_([2-9]+)")); + Q_ASSERT(pyArgsRegexCheck.isValid()); + const QRegularExpressionMatch match = pyArgsRegexCheck.match(code); + if (match.hasMatch()) { + qCWarning(lcShiboken).noquote().nospace() + << msgWrongIndex("%PYARG", match.captured(1), func); + return; + } + code.replace(QLatin1String("%PYARG_1"), QLatin1String(PYTHON_ARG)); + } + } else { + // Replaces the simplest case of attribution to a + // Python argument on the binding virtual method. + static const QRegularExpression pyArgsAttributionRegex(QStringLiteral("%PYARG_(\\d+)\\s*=[^=]\\s*([^;]+)")); + Q_ASSERT(pyArgsAttributionRegex.isValid()); + code.replace(pyArgsAttributionRegex, QLatin1String("PyTuple_SET_ITEM(") + + QLatin1String(PYTHON_ARGS) + QLatin1String(", \\1-1, \\2)")); + code.replace(pyArgsRegex, QLatin1String("PyTuple_GET_ITEM(") + + QLatin1String(PYTHON_ARGS) + QLatin1String(", \\1-1)")); + } + + // Replace %ARG#_TYPE variables. + const AbstractMetaArgumentList &arguments = func->arguments(); + for (const AbstractMetaArgument &arg : arguments) { + QString argTypeVar = QStringLiteral("%ARG%1_TYPE").arg(arg.argumentIndex() + 1); + QString argTypeVal = arg.type().cppSignature(); + code.replace(argTypeVar, argTypeVal); + } + + static const QRegularExpression cppArgTypeRegexCheck(QStringLiteral("%ARG(\\d+)_TYPE")); + Q_ASSERT(cppArgTypeRegexCheck.isValid()); + QRegularExpressionMatchIterator rit = cppArgTypeRegexCheck.globalMatch(code); + while (rit.hasNext()) { + QRegularExpressionMatch match = rit.next(); + qCWarning(lcShiboken).noquote().nospace() + << msgWrongIndex("%ARG#_TYPE", match.captured(1), func); + } + + // Replace template variable for return variable name. + if (func->isConstructor()) { + code.replace(QLatin1String("%0."), QLatin1String("cptr->")); + code.replace(QLatin1String("%0"), QLatin1String("cptr")); + } else if (!func->isVoid()) { + QString returnValueOp = isPointerToWrapperType(func->type()) + ? QLatin1String("%1->") : QLatin1String("%1."); + if (ShibokenGenerator::isWrapperType(func->type())) + code.replace(QLatin1String("%0."), returnValueOp.arg(QLatin1String(CPP_RETURN_VAR))); + code.replace(QLatin1String("%0"), QLatin1String(CPP_RETURN_VAR)); + } + + // Replace template variable for self Python object. + QString pySelf = language == TypeSystem::NativeCode + ? QLatin1String("pySelf") : QLatin1String("self"); + code.replace(QLatin1String("%PYSELF"), pySelf); + + // Replace template variable for a pointer to C++ of this object. + if (func->implementingClass()) { + QString replacement = func->isStatic() ? QLatin1String("%1::") : QLatin1String("%1->"); + QString cppSelf; + if (func->isStatic()) + cppSelf = func->ownerClass()->qualifiedCppName(); + else if (language == TypeSystem::NativeCode) + cppSelf = QLatin1String("this"); + else + cppSelf = QLatin1String(CPP_SELF_VAR); + + // On comparison operator CPP_SELF_VAR is always a reference. + if (func->isComparisonOperator()) + replacement = QLatin1String("%1."); + + if (func->isVirtual() && !func->isAbstract() && (!avoidProtectedHack() || !func->isProtected())) { + QString methodCallArgs = getArgumentsFromMethodCall(code); + if (!methodCallArgs.isEmpty()) { + const QString pattern = QStringLiteral("%CPPSELF.%FUNCTION_NAME(%1)").arg(methodCallArgs); + if (func->name() == QLatin1String("metaObject")) { + QString wrapperClassName = wrapperName(func->ownerClass()); + QString cppSelfVar = avoidProtectedHack() + ? QLatin1String("%CPPSELF") + : QStringLiteral("reinterpret_cast<%1 *>(%CPPSELF)").arg(wrapperClassName); + code.replace(pattern, + QString::fromLatin1("(Shiboken::Object::hasCppWrapper(reinterpret_cast<SbkObject *>(%1))" + " ? %2->::%3::%FUNCTION_NAME(%4)" + " : %CPPSELF.%FUNCTION_NAME(%4))").arg(pySelf, cppSelfVar, wrapperClassName, methodCallArgs)); + } else { + code.replace(pattern, + QString::fromLatin1("(Shiboken::Object::hasCppWrapper(reinterpret_cast<SbkObject *>(%1))" + " ? %CPPSELF->::%TYPE::%FUNCTION_NAME(%2)" + " : %CPPSELF.%FUNCTION_NAME(%2))").arg(pySelf, methodCallArgs)); + } + } + } + + code.replace(QLatin1String("%CPPSELF."), replacement.arg(cppSelf)); + code.replace(QLatin1String("%CPPSELF"), cppSelf); + + if (code.indexOf(QLatin1String("%BEGIN_ALLOW_THREADS")) > -1) { + if (code.count(QLatin1String("%BEGIN_ALLOW_THREADS")) == code.count(QLatin1String("%END_ALLOW_THREADS"))) { + code.replace(QLatin1String("%BEGIN_ALLOW_THREADS"), QLatin1String(BEGIN_ALLOW_THREADS)); + code.replace(QLatin1String("%END_ALLOW_THREADS"), QLatin1String(END_ALLOW_THREADS)); + } else { + qCWarning(lcShiboken) << "%BEGIN_ALLOW_THREADS and %END_ALLOW_THREADS mismatch"; + } + } + + // replace template variable for the Python Type object for the + // class implementing the method in which the code snip is written + if (func->isStatic()) { + code.replace(QLatin1String("%PYTHONTYPEOBJECT"), + cpythonTypeName(func->implementingClass()) + QLatin1String("->type")); + } else { + code.replace(QLatin1String("%PYTHONTYPEOBJECT."), pySelf + QLatin1String("->ob_type->")); + code.replace(QLatin1String("%PYTHONTYPEOBJECT"), pySelf + QLatin1String("->ob_type")); + } + } + + // Replaces template %ARGUMENT_NAMES and %# variables by argument variables and values. + // Replaces template variables %# for individual arguments. + const ArgumentVarReplacementList &argReplacements = getArgumentReplacement(func, usePyArgs, language, lastArg); + + QStringList args; + for (const ArgumentVarReplacementPair &pair : argReplacements) { + if (pair.second.startsWith(QLatin1String(CPP_ARG_REMOVED))) + continue; + args << pair.second; + } + code.replace(QLatin1String("%ARGUMENT_NAMES"), args.join(QLatin1String(", "))); + + for (const ArgumentVarReplacementPair &pair : argReplacements) { + const AbstractMetaArgument &arg = pair.first; + int idx = arg.argumentIndex() + 1; + AbstractMetaType type = arg.type(); + QString typeReplaced = func->typeReplaced(arg.argumentIndex() + 1); + if (!typeReplaced.isEmpty()) { + AbstractMetaType builtType = buildAbstractMetaTypeFromString(typeReplaced); + if (builtType) + type = builtType; + } + if (isWrapperType(type)) { + QString replacement = pair.second; + if (type.referenceType() == LValueReference && !isPointer(type)) + replacement.remove(0, 1); + if (type.referenceType() == LValueReference || isPointer(type)) + code.replace(QString::fromLatin1("%%1.").arg(idx), replacement + QLatin1String("->")); + } + code.replace(placeHolderRegex(idx), pair.second); + } + + if (language == TypeSystem::NativeCode) { + // Replaces template %PYTHON_ARGUMENTS variable with a pointer to the Python tuple + // containing the converted virtual method arguments received from C++ to be passed + // to the Python override. + code.replace(QLatin1String("%PYTHON_ARGUMENTS"), QLatin1String(PYTHON_ARGS)); + + // replace variable %PYTHON_METHOD_OVERRIDE for a pointer to the Python method + // override for the C++ virtual method in which this piece of code was inserted + code.replace(QLatin1String("%PYTHON_METHOD_OVERRIDE"), QLatin1String(PYTHON_OVERRIDE_VAR)); + } + + if (avoidProtectedHack()) { + // If the function being processed was added by the user via type system, + // Shiboken needs to find out if there are other overloads for the same method + // name and if any of them is of the protected visibility. This is used to replace + // calls to %FUNCTION_NAME on user written custom code for calls to the protected + // dispatcher. + bool hasProtectedOverload = false; + if (func->isUserAdded()) { + const AbstractMetaFunctionList &funcs = getFunctionOverloads(func->ownerClass(), func->name()); + for (const AbstractMetaFunction *f : funcs) + hasProtectedOverload |= f->isProtected(); + } + + if (func->isProtected() || hasProtectedOverload) { + code.replace(QLatin1String("%TYPE::%FUNCTION_NAME"), + QStringLiteral("%1::%2_protected") + .arg(wrapperName(func->ownerClass()), func->originalName())); + code.replace(QLatin1String("%FUNCTION_NAME"), + func->originalName() + QLatin1String("_protected")); + } + } + + if (func->isConstructor() && shouldGenerateCppWrapper(func->ownerClass())) + code.replace(QLatin1String("%TYPE"), wrapperName(func->ownerClass())); + + if (func->ownerClass()) + code.replace(QLatin1String("%CPPTYPE"), func->ownerClass()->name()); + + replaceTemplateVariables(code, func); + + processCodeSnip(code); + s << INDENT << "// Begin code injection\n"; + s << code; + s << INDENT << "// End of code injection\n\n"; +} + +// Returns true if the string is an expression, +// and false if it is a variable. +static bool isVariable(const QString &code) +{ + static const QRegularExpression expr(QStringLiteral("^\\s*\\*?\\s*[A-Za-z_][A-Za-z_0-9.]*\\s*(?:\\[[^\\[]+\\])*$")); + Q_ASSERT(expr.isValid()); + return expr.match(code.trimmed()).hasMatch(); +} + +// A miniature normalizer that puts a type string into a format +// suitable for comparison with AbstractMetaType::cppSignature() +// result. +static QString miniNormalizer(const QString &varType) +{ + QString normalized = varType.trimmed(); + if (normalized.isEmpty()) + return normalized; + if (normalized.startsWith(QLatin1String("::"))) + normalized.remove(0, 2); + QString suffix; + while (normalized.endsWith(QLatin1Char('*')) || normalized.endsWith(QLatin1Char('&'))) { + suffix.prepend(normalized.at(normalized.count() - 1)); + normalized.chop(1); + normalized = normalized.trimmed(); + } + const QString result = normalized + QLatin1Char(' ') + suffix; + return result.trimmed(); +} +// The position must indicate the first character after the opening '('. +// ATTENTION: do not modify this function to trim any resulting string! +// This must be done elsewhere. +static QString getConverterTypeSystemVariableArgument(const QString &code, int pos) +{ + QString arg; + int parenthesisDepth = 0; + int count = 0; + while (pos + count < code.count()) { + char c = code.at(pos+count).toLatin1(); // toAscii is gone + if (c == '(') { + ++parenthesisDepth; + } else if (c == ')') { + if (parenthesisDepth == 0) { + arg = code.mid(pos, count).trimmed(); + break; + } + --parenthesisDepth; + } + ++count; + } + if (parenthesisDepth != 0) + qFatal("Unbalanced parenthesis on type system converter variable call."); + return arg; +} +using StringPair = QPair<QString, QString>; + +void ShibokenGenerator::replaceConverterTypeSystemVariable(TypeSystemConverterVariable converterVariable, QString &code) +{ + QVector<StringPair> replacements; + QRegularExpressionMatchIterator rit = m_typeSystemConvRegEx[converterVariable].globalMatch(code); + while (rit.hasNext()) { + const QRegularExpressionMatch match = rit.next(); + const QStringList list = match.capturedTexts(); + QString conversionString = list.constFirst(); + const QString &conversionTypeName = list.constLast(); + QString message; + const AbstractMetaType conversionType = buildAbstractMetaTypeFromString(conversionTypeName, &message); + if (!conversionType) { + qFatal("%s", qPrintable(msgCannotFindType(conversionTypeName, + m_typeSystemConvName[converterVariable], + message))); + } + QString conversion; + QTextStream c(&conversion); + switch (converterVariable) { + case TypeSystemToCppFunction: { + int end = match.capturedStart(); + int start = end; + while (start > 0 && code.at(start) != QLatin1Char('\n')) + --start; + while (code.at(start).isSpace()) + ++start; + QString varType = code.mid(start, end - start); + conversionString = varType + list.constFirst(); + varType = miniNormalizer(varType); + QString varName = list.at(1).trimmed(); + if (!varType.isEmpty()) { + const QString conversionSignature = conversionType.cppSignature(); + if (varType != QLatin1String("auto") && varType != conversionSignature) + qFatal("%s", qPrintable(msgConversionTypesDiffer(varType, conversionSignature))); + c << getFullTypeName(conversionType) << ' ' << varName; + writeMinimalConstructorExpression(c, conversionType); + c << ";\n"; + Indentation indent(INDENT); + c << INDENT; + } + c << cpythonToCppConversionFunction(conversionType); + QString prefix; + if (varName.startsWith(QLatin1Char('*'))) { + varName.remove(0, 1); + varName = varName.trimmed(); + } else { + prefix = QLatin1Char('&'); + } + QString arg = getConverterTypeSystemVariableArgument(code, match.capturedEnd()); + conversionString += arg; + c << arg << ", " << prefix << '(' << varName << ')'; + break; + } + case TypeSystemCheckFunction: + conversion = cpythonCheckFunction(conversionType); + if (conversionType.typeEntry()->isPrimitive() + && (conversionType.typeEntry()->name() == QLatin1String("PyObject") + || !conversion.endsWith(QLatin1Char(' ')))) { + c << '('; + break; + } + Q_FALLTHROUGH(); + case TypeSystemIsConvertibleFunction: + if (conversion.isEmpty()) + conversion = cpythonIsConvertibleFunction(conversionType); + Q_FALLTHROUGH(); + case TypeSystemToPythonFunction: + if (conversion.isEmpty()) + conversion = cpythonToPythonConversionFunction(conversionType); + Q_FALLTHROUGH(); + default: { + QString arg = getConverterTypeSystemVariableArgument(code, match.capturedEnd()); + conversionString += arg; + if (converterVariable == TypeSystemToPythonFunction && !isVariable(arg)) { + qFatal("Only variables are acceptable as argument to %%CONVERTTOPYTHON type system variable on code snippet: '%s'", + qPrintable(code)); + } + if (conversion.contains(QLatin1String("%in"))) { + conversion.prepend(QLatin1Char('(')); + conversion.replace(QLatin1String("%in"), arg); + } else { + c << arg; + } + } + } + replacements.append(qMakePair(conversionString, conversion)); + } + for (const StringPair &rep : qAsConst(replacements)) + code.replace(rep.first, rep.second); +} + +bool ShibokenGenerator::injectedCodeUsesPySelf(const AbstractMetaFunction *func) +{ + CodeSnipList snips = func->injectedCodeSnips(TypeSystem::CodeSnipPositionAny, TypeSystem::NativeCode); + for (const CodeSnip &snip : qAsConst(snips)) { + if (snip.code().contains(QLatin1String("%PYSELF"))) + return true; + } + return false; +} + +bool ShibokenGenerator::injectedCodeCallsCppFunction(const GeneratorContext &context, + const AbstractMetaFunction *func) +{ + QString funcCall = func->originalName() + QLatin1Char('('); + QString wrappedCtorCall; + if (func->isConstructor()) { + funcCall.prepend(QLatin1String("new ")); + const auto owner = func->ownerClass(); + const QString className = context.useWrapper() + ? context.wrapperName() : owner->qualifiedCppName(); + wrappedCtorCall = QLatin1String("new ") + className + QLatin1Char('('); + } + CodeSnipList snips = func->injectedCodeSnips(TypeSystem::CodeSnipPositionAny, TypeSystem::TargetLangCode); + for (const CodeSnip &snip : qAsConst(snips)) { + if (snip.code().contains(QLatin1String("%FUNCTION_NAME(")) || snip.code().contains(funcCall) + || (func->isConstructor() + && ((func->ownerClass()->isPolymorphic() && snip.code().contains(wrappedCtorCall)) + || snip.code().contains(QLatin1String("new %TYPE(")))) + ) + return true; + } + return false; +} + +bool ShibokenGenerator::injectedCodeCallsPythonOverride(const AbstractMetaFunction *func) +{ + static const QRegularExpression overrideCallRegexCheck(QStringLiteral("PyObject_Call\\s*\\(\\s*%PYTHON_METHOD_OVERRIDE\\s*,")); + Q_ASSERT(overrideCallRegexCheck.isValid()); + CodeSnipList snips = func->injectedCodeSnips(TypeSystem::CodeSnipPositionAny, TypeSystem::NativeCode); + for (const CodeSnip &snip : qAsConst(snips)) { + if (snip.code().contains(overrideCallRegexCheck)) + return true; + } + return false; +} + +bool ShibokenGenerator::injectedCodeHasReturnValueAttribution(const AbstractMetaFunction *func, TypeSystem::Language language) +{ + static const QRegularExpression retValAttributionRegexCheck_native(QStringLiteral("%0\\s*=[^=]\\s*.+")); + Q_ASSERT(retValAttributionRegexCheck_native.isValid()); + static const QRegularExpression retValAttributionRegexCheck_target(QStringLiteral("%PYARG_0\\s*=[^=]\\s*.+")); + Q_ASSERT(retValAttributionRegexCheck_target.isValid()); + CodeSnipList snips = func->injectedCodeSnips(TypeSystem::CodeSnipPositionAny, language); + for (const CodeSnip &snip : qAsConst(snips)) { + if (language == TypeSystem::TargetLangCode) { + if (snip.code().contains(retValAttributionRegexCheck_target)) + return true; + } else { + if (snip.code().contains(retValAttributionRegexCheck_native)) + return true; + } + } + return false; +} + +bool ShibokenGenerator::injectedCodeUsesArgument(const AbstractMetaFunction *func, int argumentIndex) +{ + CodeSnipList snips = func->injectedCodeSnips(TypeSystem::CodeSnipPositionAny); + const QRegularExpression argRegEx = placeHolderRegex(argumentIndex + 1); + for (const CodeSnip &snip : qAsConst(snips)) { + QString code = snip.code(); + if (code.contains(QLatin1String("%ARGUMENT_NAMES")) || code.contains(argRegEx)) + return true; + } + return false; +} + +bool ShibokenGenerator::useOverrideCaching(const AbstractMetaClass *metaClass) +{ + return metaClass->isPolymorphic(); +} + +ShibokenGenerator::AttroCheck ShibokenGenerator::checkAttroFunctionNeeds(const AbstractMetaClass *metaClass) const +{ + AttroCheck result; + if (metaClass->typeEntry()->isSmartPointer()) { + result |= AttroCheckFlag::GetattroSmartPointer | AttroCheckFlag::SetattroSmartPointer; + } else { + if (getGeneratorClassInfo(metaClass).needsGetattroFunction) + result |= AttroCheckFlag::GetattroOverloads; + if (metaClass->queryFirstFunction(metaClass->functions(), + AbstractMetaClass::GetAttroFunction)) { + result |= AttroCheckFlag::GetattroUser; + } + if (usePySideExtensions() && metaClass->qualifiedCppName() == qObjectT()) + result |= AttroCheckFlag::SetattroQObject; + if (useOverrideCaching(metaClass)) + result |= AttroCheckFlag::SetattroMethodOverride; + if (metaClass->queryFirstFunction(metaClass->functions(), + AbstractMetaClass::SetAttroFunction)) { + result |= AttroCheckFlag::SetattroUser; + } + // PYSIDE-1255: If setattro is generated for a class inheriting + // QObject, the property code needs to be generated, too. + if ((result & AttroCheckFlag::SetattroMask) != 0 + && !result.testFlag(AttroCheckFlag::SetattroQObject) + && metaClass->isQObject()) { + result |= AttroCheckFlag::SetattroQObject; + } + } + return result; +} + +bool ShibokenGenerator::classNeedsGetattroFunctionImpl(const AbstractMetaClass *metaClass) +{ + if (!metaClass) + return false; + if (metaClass->typeEntry()->isSmartPointer()) + return true; + const auto &functionGroup = getFunctionGroups(metaClass); + for (auto it = functionGroup.cbegin(), end = functionGroup.cend(); it != end; ++it) { + AbstractMetaFunctionList overloads; + for (AbstractMetaFunction *func : qAsConst(it.value())) { + if (func->isAssignmentOperator() || func->isCastOperator() || func->isModifiedRemoved() + || func->isPrivate() || func->ownerClass() != func->implementingClass() + || func->isConstructor() || func->isOperatorOverload()) + continue; + overloads.append(func); + } + if (overloads.isEmpty()) + continue; + if (OverloadData::hasStaticAndInstanceFunctions(overloads)) + return true; + } + return false; +} + +AbstractMetaFunctionList ShibokenGenerator::getMethodsWithBothStaticAndNonStaticMethods(const AbstractMetaClass *metaClass) +{ + AbstractMetaFunctionList methods; + if (metaClass) { + const auto &functionGroups = getFunctionGroups(metaClass); + for (auto it = functionGroups.cbegin(), end = functionGroups.cend(); it != end; ++it) { + AbstractMetaFunctionList overloads; + for (AbstractMetaFunction *func : qAsConst(it.value())) { + if (func->isAssignmentOperator() || func->isCastOperator() || func->isModifiedRemoved() + || func->isPrivate() || func->ownerClass() != func->implementingClass() + || func->isConstructor() || func->isOperatorOverload()) + continue; + overloads.append(func); + } + if (overloads.isEmpty()) + continue; + if (OverloadData::hasStaticAndInstanceFunctions(overloads)) + methods.append(overloads.constFirst()); + } + } + return methods; +} + +AbstractMetaClassList ShibokenGenerator::getBaseClasses(const AbstractMetaClass *metaClass) const +{ + AbstractMetaClassList baseClasses; + if (metaClass) { + QStringList baseClassNames(metaClass->baseClassNames()); + const QString defaultSuperclass = metaClass->typeEntry()->defaultSuperclass(); + if (!defaultSuperclass.isEmpty()) { + int index = baseClassNames.indexOf(defaultSuperclass); + if (index >= 0) + baseClassNames.move(index, 0); + } + for (const QString &parent : baseClassNames) { + AbstractMetaClass *clazz = AbstractMetaClass::findClass(classes(), parent); + if (clazz) + baseClasses << clazz; + } + } + return baseClasses; +} + +const AbstractMetaClass *ShibokenGenerator::getMultipleInheritingClass(const AbstractMetaClass *metaClass) +{ + if (!metaClass || metaClass->baseClassNames().isEmpty()) + return nullptr; + if (metaClass->baseClassNames().size() > 1) + return metaClass; + return getMultipleInheritingClass(metaClass->baseClass()); +} + +AbstractMetaClassList ShibokenGenerator::getAllAncestors(const AbstractMetaClass *metaClass) const +{ + AbstractMetaClassList result; + if (metaClass) { + AbstractMetaClassList baseClasses = getBaseClasses(metaClass); + for (AbstractMetaClass *base : qAsConst(baseClasses)) { + result.append(base); + result.append(getAllAncestors(base)); + } + } + return result; +} + +QString ShibokenGenerator::getModuleHeaderFileName(const QString &moduleName) const +{ + return moduleCppPrefix(moduleName).toLower() + QLatin1String("_python.h"); +} + +bool ShibokenGenerator::isCopyable(const AbstractMetaClass *metaClass) + +{ + if (metaClass->isNamespace() || isObjectType(metaClass)) + return false; + if (metaClass->typeEntry()->copyable() == ComplexTypeEntry::Unknown) + return metaClass->hasCloneOperator(); + + return metaClass->typeEntry()->copyable() == ComplexTypeEntry::CopyableSet; +} + +AbstractMetaType ShibokenGenerator::buildAbstractMetaTypeFromString(QString typeSignature, + QString *errorMessage) +{ + typeSignature = typeSignature.trimmed(); + if (typeSignature.startsWith(QLatin1String("::"))) + typeSignature.remove(0, 2); + + auto it = m_metaTypeFromStringCache.find(typeSignature); + if (it == m_metaTypeFromStringCache.end()) { + AbstractMetaType metaType = + AbstractMetaBuilder::translateType(typeSignature, nullptr, {}, errorMessage); + if (Q_UNLIKELY(!metaType)) { + if (errorMessage) + errorMessage->prepend(msgCannotBuildMetaType(typeSignature)); + return {}; + } + it = m_metaTypeFromStringCache.insert(typeSignature, metaType); + } + return it.value(); +} + +AbstractMetaType ShibokenGenerator::buildAbstractMetaTypeFromTypeEntry(const TypeEntry *typeEntry) +{ + QString typeName = typeEntry->qualifiedCppName(); + if (typeName.startsWith(QLatin1String("::"))) + typeName.remove(0, 2); + if (m_metaTypeFromStringCache.contains(typeName)) + return m_metaTypeFromStringCache.value(typeName); + AbstractMetaType metaType(typeEntry); + metaType.clearIndirections(); + metaType.setReferenceType(NoReference); + metaType.setConstant(false); + metaType.decideUsagePattern(); + m_metaTypeFromStringCache.insert(typeName, metaType); + return metaType; +} +AbstractMetaType ShibokenGenerator::buildAbstractMetaTypeFromAbstractMetaClass(const AbstractMetaClass *metaClass) +{ + return ShibokenGenerator::buildAbstractMetaTypeFromTypeEntry(metaClass->typeEntry()); +} + +/* +static void dumpFunction(AbstractMetaFunctionList lst) +{ + qDebug() << "DUMP FUNCTIONS: "; + for (AbstractMetaFunction *func : qAsConst(lst)) + qDebug() << "*" << func->ownerClass()->name() + << func->signature() + << "Private: " << func->isPrivate() + << "Empty: " << func->isEmptyFunction() + << "Static:" << func->isStatic() + << "Signal:" << func->isSignal() + << "ClassImplements: " << (func->ownerClass() != func->implementingClass()) + << "is operator:" << func->isOperatorOverload() + << "is global:" << func->isInGlobalScope(); +} +*/ + +static bool isGroupable(const AbstractMetaFunction *func) +{ + switch (func->functionType()) { + case AbstractMetaFunction::DestructorFunction: + case AbstractMetaFunction::SignalFunction: + case AbstractMetaFunction::GetAttroFunction: + case AbstractMetaFunction::SetAttroFunction: + return false; + default: + break; + } + if (func->isModifiedRemoved() && !func->isAbstract()) + return false; + // weird operator overloads + if (func->name() == QLatin1String("operator[]") || func->name() == QLatin1String("operator->")) // FIXME: what about cast operators? + return false; + return true; +} + +static void insertIntoFunctionGroups(const AbstractMetaFunctionList &lst, + ShibokenGenerator::FunctionGroups *results) +{ + for (AbstractMetaFunction *func : lst) { + if (isGroupable(func)) + (*results)[func->name()].append(func); + } +} + +ShibokenGenerator::FunctionGroups ShibokenGenerator::getGlobalFunctionGroups() const +{ + FunctionGroups results; + insertIntoFunctionGroups(globalFunctions(), &results); + for (auto nsp : invisibleTopNamespaces()) + insertIntoFunctionGroups(nsp->functions(), &results); + return results; +} + +const GeneratorClassInfoCacheEntry &ShibokenGenerator::getGeneratorClassInfo(const AbstractMetaClass *scope) +{ + auto cache = generatorClassInfoCache(); + auto it = cache->find(scope); + if (it == cache->end()) { + it = cache->insert(scope, {}); + it.value().functionGroups = getFunctionGroupsImpl(scope); + it.value().needsGetattroFunction = classNeedsGetattroFunctionImpl(scope); + } + return it.value(); +} + +ShibokenGenerator::FunctionGroups ShibokenGenerator::getFunctionGroups(const AbstractMetaClass *scope) +{ + Q_ASSERT(scope); + return getGeneratorClassInfo(scope).functionGroups; +} + +ShibokenGenerator::FunctionGroups ShibokenGenerator::getFunctionGroupsImpl(const AbstractMetaClass *scope) +{ + AbstractMetaFunctionList lst = scope->functions(); + scope->getFunctionsFromInvisibleNamespacesToBeGenerated(&lst); + + FunctionGroups results; + for (AbstractMetaFunction *func : lst) { + if (isGroupable(func)) { + auto it = results.find(func->name()); + if (it == results.end()) { + results.insert(func->name(), AbstractMetaFunctionList(1, func)); + } else { + // If there are virtuals methods in the mix (PYSIDE-570, + // QFileSystemModel::index(QString,int) and + // QFileSystemModel::index(int,int,QModelIndex)) override, make sure + // the overriding method of the most-derived class is seen first + // and inserted into the "seenSignatures" set. + if (func->isVirtual()) + it.value().prepend(func); + else + it.value().append(func); + } + } + } + return results; +} + +AbstractMetaFunctionList ShibokenGenerator::getInheritedOverloads(const AbstractMetaFunction *func, QSet<QString> *seen) +{ + AbstractMetaFunctionList results; + AbstractMetaClass *basis; + if (func->ownerClass() && (basis = func->ownerClass()->baseClass())) { + for (; basis; basis = basis->baseClass()) { + const AbstractMetaFunction *inFunc = basis->findFunction(func->name()); + if (inFunc && !seen->contains(inFunc->minimalSignature())) { + seen->insert(inFunc->minimalSignature()); + AbstractMetaFunction *newFunc = inFunc->copy(); + newFunc->setImplementingClass(func->implementingClass()); + results << newFunc; + } + } + } + return results; +} + +AbstractMetaFunctionList ShibokenGenerator::getFunctionAndInheritedOverloads(const AbstractMetaFunction *func, QSet<QString> *seen) +{ + AbstractMetaFunctionList results; + seen->insert(func->minimalSignature()); + results << const_cast<AbstractMetaFunction *>(func) << getInheritedOverloads(func, seen); + return results; +} + +AbstractMetaFunctionList ShibokenGenerator::getFunctionOverloads(const AbstractMetaClass *scope, const QString &functionName) +{ + AbstractMetaFunctionList lst = scope ? scope->functions() : globalFunctions(); + + AbstractMetaFunctionList results; + QSet<QString> seenSignatures; + for (AbstractMetaFunction *func : qAsConst(lst)) { + if (func->name() != functionName) + continue; + if (isGroupable(func)) { + // PYSIDE-331: look also into base classes. + results << getFunctionAndInheritedOverloads(func, &seenSignatures); + } + } + return results; +} + +Generator::OptionDescriptions ShibokenGenerator::options() const +{ + return OptionDescriptions() + << qMakePair(QLatin1String(AVOID_PROTECTED_HACK), + QLatin1String("Avoid the use of the '#define protected public' hack.")) + << qMakePair(QLatin1String(DISABLE_VERBOSE_ERROR_MESSAGES), + QLatin1String("Disable verbose error messages. Turn the python code hard to debug\n" + "but safe few kB on the generated bindings.")) + << qMakePair(QLatin1String(PARENT_CTOR_HEURISTIC), + QLatin1String("Enable heuristics to detect parent relationship on constructors.")) + << qMakePair(QLatin1String(ENABLE_PYSIDE_EXTENSIONS), + QLatin1String("Enable PySide extensions, such as support for signal/slots,\n" + "use this if you are creating a binding for a Qt-based library.")) + << qMakePair(QLatin1String(RETURN_VALUE_HEURISTIC), + QLatin1String("Enable heuristics to detect parent relationship on return values\n" + "(USE WITH CAUTION!)")) + << qMakePair(QLatin1String(USE_ISNULL_AS_NB_NONZERO), + QLatin1String("If a class have an isNull() const method, it will be used to compute\n" + "the value of boolean casts")) + << qMakePair(QLatin1String(WRAPPER_DIAGNOSTICS), + QLatin1String("Generate diagnostic code around wrappers")); +} + +bool ShibokenGenerator::handleOption(const QString &key, const QString & /* value */) +{ + if (key == QLatin1String(PARENT_CTOR_HEURISTIC)) + return (m_useCtorHeuristic = true); + if (key == QLatin1String(ENABLE_PYSIDE_EXTENSIONS)) + return (m_usePySideExtensions = true); + if (key == QLatin1String(RETURN_VALUE_HEURISTIC)) + return (m_userReturnValueHeuristic = true); + if (key == QLatin1String(DISABLE_VERBOSE_ERROR_MESSAGES)) + return (m_verboseErrorMessagesDisabled = true); + if (key == QLatin1String(USE_ISNULL_AS_NB_NONZERO)) + return (m_useIsNullAsNbNonZero = true); + if (key == QLatin1String(AVOID_PROTECTED_HACK)) + return (m_avoidProtectedHack = true); + if (key == QLatin1String(WRAPPER_DIAGNOSTICS)) + return (m_wrapperDiagnostics = true); + return false; +} + +static void getCode(QStringList &code, const CodeSnipList &codeSnips) +{ + for (const CodeSnip &snip : qAsConst(codeSnips)) + code.append(snip.code()); +} + +static void getCode(QStringList &code, const TypeEntry *type) +{ + getCode(code, type->codeSnips()); + + CustomConversion *customConversion = type->customConversion(); + if (!customConversion) + return; + + if (!customConversion->nativeToTargetConversion().isEmpty()) + code.append(customConversion->nativeToTargetConversion()); + + const CustomConversion::TargetToNativeConversions &toCppConversions = customConversion->targetToNativeConversions(); + if (toCppConversions.isEmpty()) + return; + + for (CustomConversion::TargetToNativeConversion *toNative : qAsConst(toCppConversions)) + code.append(toNative->conversion()); +} + +bool ShibokenGenerator::doSetup() +{ + QStringList snips; + const PrimitiveTypeEntryList &primitiveTypeList = primitiveTypes(); + for (const PrimitiveTypeEntry *type : primitiveTypeList) + getCode(snips, type); + const ContainerTypeEntryList &containerTypeList = containerTypes(); + for (const ContainerTypeEntry *type : containerTypeList) + getCode(snips, type); + for (const AbstractMetaClass *metaClass : classes()) + getCode(snips, metaClass->typeEntry()); + + const TypeSystemTypeEntry *moduleEntry = TypeDatabase::instance()->defaultTypeSystemType(); + Q_ASSERT(moduleEntry); + getCode(snips, moduleEntry); + + const auto &functionGroups = getGlobalFunctionGroups(); + for (auto it = functionGroups.cbegin(), end = functionGroups.cend(); it != end; ++it) { + for (AbstractMetaFunction *func : it.value()) + getCode(snips, func->injectedCodeSnips()); + } + + for (const QString &code : qAsConst(snips)) { + collectContainerTypesFromConverterMacros(code, true); + collectContainerTypesFromConverterMacros(code, false); + } + + return true; +} + +void ShibokenGenerator::collectContainerTypesFromConverterMacros(const QString &code, bool toPythonMacro) +{ + QString convMacro = toPythonMacro ? QLatin1String("%CONVERTTOPYTHON[") : QLatin1String("%CONVERTTOCPP["); + int offset = toPythonMacro ? sizeof("%CONVERTTOPYTHON") : sizeof("%CONVERTTOCPP"); + int start = 0; + QString errorMessage; + while ((start = code.indexOf(convMacro, start)) != -1) { + int end = code.indexOf(QLatin1Char(']'), start); + start += offset; + if (code.at(start) != QLatin1Char('%')) { + QString typeString = code.mid(start, end - start); + if (AbstractMetaType type = + buildAbstractMetaTypeFromString(typeString, &errorMessage)) { + addInstantiatedContainersAndSmartPointers(type, type.originalTypeDescription()); + } else { + qFatal("%s: Cannot translate type \"%s\": %s", __FUNCTION__, + qPrintable(typeString), qPrintable(errorMessage)); + } + } + start = end; + } +} + +bool ShibokenGenerator::useCtorHeuristic() const +{ + return m_useCtorHeuristic; +} + +bool ShibokenGenerator::useReturnValueHeuristic() const +{ + return m_userReturnValueHeuristic; +} + +bool ShibokenGenerator::usePySideExtensions() const +{ + return m_usePySideExtensions; +} + +bool ShibokenGenerator::useIsNullAsNbNonZero() const +{ + return m_useIsNullAsNbNonZero; +} + +bool ShibokenGenerator::avoidProtectedHack() const +{ + return m_avoidProtectedHack; +} + +QString ShibokenGenerator::moduleCppPrefix(const QString &moduleName) const + { + QString result = moduleName.isEmpty() ? packageName() : moduleName; + result.replace(QLatin1Char('.'), QLatin1Char('_')); + return result; +} + +QString ShibokenGenerator::cppApiVariableName(const QString &moduleName) const +{ + return QLatin1String("Sbk") + moduleCppPrefix(moduleName) + + QLatin1String("Types"); +} + +QString ShibokenGenerator::pythonModuleObjectName(const QString &moduleName) const +{ + return QLatin1String("Sbk") + moduleCppPrefix(moduleName) + + QLatin1String("ModuleObject"); +} + +QString ShibokenGenerator::convertersVariableName(const QString &moduleName) const +{ + QString result = cppApiVariableName(moduleName); + result.chop(1); + result.append(QLatin1String("Converters")); + return result; +} + +static QString processInstantiationsVariableName(const AbstractMetaType &type) +{ + QString res = QLatin1Char('_') + _fixedCppTypeName(type.typeEntry()->qualifiedCppName()).toUpper(); + for (const auto &instantiation : type.instantiations()) { + res += instantiation.isContainer() + ? processInstantiationsVariableName(instantiation) + : QLatin1Char('_') + _fixedCppTypeName(instantiation.cppSignature()).toUpper(); + } + return res; +} + +static void appendIndexSuffix(QString *s) +{ + if (!s->endsWith(QLatin1Char('_'))) + s->append(QLatin1Char('_')); + s->append(QStringLiteral("IDX")); +} + +QString ShibokenGenerator::getTypeIndexVariableName(const AbstractMetaClass *metaClass, + bool alternativeTemplateName) const +{ + if (alternativeTemplateName) { + const AbstractMetaClass *templateBaseClass = metaClass->templateBaseClass(); + if (!templateBaseClass) + return QString(); + QString result = QLatin1String("SBK_") + + _fixedCppTypeName(templateBaseClass->typeEntry()->qualifiedCppName()).toUpper(); + for (const auto &instantiation : metaClass->templateBaseClassInstantiations()) + result += processInstantiationsVariableName(instantiation); + appendIndexSuffix(&result); + return result; + } + return getTypeIndexVariableName(metaClass->typeEntry()); +} +QString ShibokenGenerator::getTypeIndexVariableName(const TypeEntry *type) const +{ + if (type->isCppPrimitive()) { + const auto *trueType = static_cast<const PrimitiveTypeEntry *>(type); + if (trueType->basicReferencedTypeEntry()) + type = trueType->basicReferencedTypeEntry(); + } + QString result = QLatin1String("SBK_"); + // Disambiguate namespaces per module to allow for extending them. + if (type->isNamespace()) { + QString package = type->targetLangPackage(); + const int dot = package.lastIndexOf(QLatin1Char('.')); + result += QStringView{package}.right(package.size() - (dot + 1)); + } + result += _fixedCppTypeName(type->qualifiedCppName()).toUpper(); + appendIndexSuffix(&result); + return result; +} +QString ShibokenGenerator::getTypeIndexVariableName(const AbstractMetaType &type) const +{ + QString result = QLatin1String("SBK"); + if (type.typeEntry()->isContainer()) + result += QLatin1Char('_') + moduleName().toUpper(); + result += processInstantiationsVariableName(type); + appendIndexSuffix(&result); + return result; +} + +bool ShibokenGenerator::verboseErrorMessagesDisabled() const +{ + return m_verboseErrorMessagesDisabled; +} + +bool ShibokenGenerator::pythonFunctionWrapperUsesListOfArguments(const OverloadData &overloadData) +{ + if (overloadData.referenceFunction()->isCallOperator()) + return true; + if (overloadData.referenceFunction()->isOperatorOverload()) + return false; + int maxArgs = overloadData.maxArgs(); + int minArgs = overloadData.minArgs(); + return (minArgs != maxArgs) + || (maxArgs > 1) + || overloadData.referenceFunction()->isConstructor() + || overloadData.hasArgumentWithDefaultValue(); +} + +void ShibokenGenerator::writeMinimalConstructorExpression(QTextStream &s, + const AbstractMetaType &type, + const QString &defaultCtor) +{ + if (!defaultCtor.isEmpty()) { + s << " = " << defaultCtor; + return; + } + if (isCppPrimitive(type) || type.isSmartPointer()) + return; + const auto ctor = minimalConstructor(type); + if (ctor.isValid()) { + s << ctor.initialization(); + } else { + const QString message = msgCouldNotFindMinimalConstructor(QLatin1String(__FUNCTION__), type.cppSignature()); + qCWarning(lcShiboken()).noquote() << message; + s << ";\n#error " << message << '\n'; + } +} + +void ShibokenGenerator::writeMinimalConstructorExpression(QTextStream &s, const TypeEntry *type, const QString &defaultCtor) +{ + if (!defaultCtor.isEmpty()) { + s << " = " << defaultCtor; + return; + } + if (isCppPrimitive(type)) + return; + const auto ctor = minimalConstructor(type); + if (ctor.isValid()) { + s << ctor.initialization(); + } else { + const QString message = msgCouldNotFindMinimalConstructor(QLatin1String(__FUNCTION__), type->qualifiedCppName()); + qCWarning(lcShiboken()).noquote() << message; + s << ";\n#error " << message << Qt::endl; + } +} + +bool ShibokenGenerator::isCppIntegralPrimitive(const TypeEntry *type) +{ + if (!type->isCppPrimitive()) + return false; + const auto *trueType = static_cast<const PrimitiveTypeEntry *>(type); + if (trueType->basicReferencedTypeEntry()) + trueType = trueType->basicReferencedTypeEntry(); + QString typeName = trueType->qualifiedCppName(); + return !typeName.contains(QLatin1String("double")) + && !typeName.contains(QLatin1String("float")) + && !typeName.contains(QLatin1String("wchar")); +} +bool ShibokenGenerator::isCppIntegralPrimitive(const AbstractMetaType &type) +{ + return isCppIntegralPrimitive(type.typeEntry()); +} + +QString ShibokenGenerator::pythonArgsAt(int i) +{ + return QLatin1String(PYTHON_ARGS) + QLatin1Char('[') + + QString::number(i) + QLatin1Char(']'); +} diff --git a/sources/shiboken6/generator/shiboken/shibokengenerator.h b/sources/shiboken6/generator/shiboken/shibokengenerator.h new file mode 100644 index 000000000..0d49764ab --- /dev/null +++ b/sources/shiboken6/generator/shiboken/shibokengenerator.h @@ -0,0 +1,585 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef SHIBOKENGENERATOR_H +#define SHIBOKENGENERATOR_H + +extern const char *CPP_ARG; +extern const char *CPP_ARG_REMOVED; +extern const char *CPP_RETURN_VAR; +extern const char *CPP_SELF_VAR; +extern const char *NULL_PTR; +extern const char *PYTHON_ARG; +extern const char *PYTHON_ARGS; +extern const char *PYTHON_OVERRIDE_VAR; +extern const char *PYTHON_RETURN_VAR; +extern const char *PYTHON_TO_CPP_VAR; +extern const char *SMART_POINTER_GETTER; + +extern const char *CONV_RULE_OUT_VAR_SUFFIX; +extern const char *BEGIN_ALLOW_THREADS; +extern const char *END_ALLOW_THREADS; + +#include <generator.h> + +#include "typesystem.h" + +#include <QtCore/QRegularExpression> + +class DocParser; +class CodeSnip; +class QPropertySpec; +class OverloadData; +struct GeneratorClassInfoCacheEntry; + +QT_FORWARD_DECLARE_CLASS(QTextStream) + +/** + * Abstract generator that contains common methods used in CppGenerator and HeaderGenerator. + */ +class ShibokenGenerator : public Generator +{ +public: + enum class AttroCheckFlag + { + None = 0x0, + GetattroOverloads = 0x01, + GetattroSmartPointer = 0x02, + GetattroUser = 0x04, // Injected code + GetattroMask = 0x0F, + SetattroQObject = 0x10, + SetattroSmartPointer = 0x20, + SetattroMethodOverride = 0x40, + SetattroUser = 0x80, // Injected code + SetattroMask = 0xF0, + }; + Q_DECLARE_FLAGS(AttroCheck, AttroCheckFlag); + + using FunctionGroups = QMap<QString, AbstractMetaFunctionList>; // Sorted + + ShibokenGenerator(); + ~ShibokenGenerator() override; + + const char *name() const override { return "Shiboken"; } + + /// Returns a list of all ancestor classes for the given class. + AbstractMetaClassList getAllAncestors(const AbstractMetaClass *metaClass) const; + + /// Returns true if the user enabled PySide extensions. + bool usePySideExtensions() const; + +protected: + bool doSetup() override; + + void writeArgumentNames(QTextStream &s, + const AbstractMetaFunction *func, + Options options = NoOption) const override; + + /** + * Function used to write the fucntion arguments on the class buffer. + * \param s the class output buffer + * \param func the pointer to metafunction information + * \param count the number of function arguments + * \param options some extra options used during the parser + */ + void writeFunctionArguments(QTextStream &s, + const AbstractMetaFunction *func, + Options options = NoOption) const override; + + GeneratorContext contextForClass(const AbstractMetaClass *c) const override; + + /** + * Returns a map with all functions grouped, the function name is used as key. + * Example of return value: { "foo" -> ["foo(int)", "foo(int, long)], "bar" -> "bar(double)"} + * \param scope Where to search for functions, null means all global functions. + */ + FunctionGroups getGlobalFunctionGroups() const; + static FunctionGroups getFunctionGroups(const AbstractMetaClass *scope); + + /** + * Returns all different inherited overloads of func, and includes func as well. + * The function can be called multiple times without duplication. + * \param func the metafunction to be searched in subclasses. + * \param seen the function's minimal signatures already seen. + */ + AbstractMetaFunctionList getFunctionAndInheritedOverloads(const AbstractMetaFunction *func, QSet<QString> *seen); + + /// Write user's custom code snippets at class or module level. + void writeClassCodeSnips(QTextStream &s, + const QVector<CodeSnip> & codeSnips, + TypeSystem::CodeSnipPosition position, + TypeSystem::Language language, + const GeneratorContext &context); + void writeCodeSnips(QTextStream &s, + const QVector<CodeSnip> & codeSnips, + TypeSystem::CodeSnipPosition position, + TypeSystem::Language language); + /// Write user's custom code snippets at function level. + void writeCodeSnips(QTextStream &s, + const QVector<CodeSnip> & codeSnips, + TypeSystem::CodeSnipPosition position, + TypeSystem::Language language, + const AbstractMetaFunction *func, + const AbstractMetaArgument *lastArg = nullptr); + + /// Replaces variables for the user's custom code at global or class level. + void processCodeSnip(QString &code); + void processClassCodeSnip(QString &code, const GeneratorContext &context); + + /** + * Verifies if any of the function's code injections of the "native" + * type needs the type system variable "%PYSELF". + * \param func the function to check + * \return true if the function's native code snippets use "%PYSELF" + */ + bool injectedCodeUsesPySelf(const AbstractMetaFunction *func); + + /** + * Verifies if any of the function's code injections makes a call + * to the C++ method. This is used by the generator to avoid writing calls + * to C++ when the user custom code already does this. + * \param func the function to check + * \return true if the function's code snippets call the wrapped C++ function + */ + bool injectedCodeCallsCppFunction(const GeneratorContext &context, + const AbstractMetaFunction *func); + + /** + * Verifies if any of the function's code injections of the "native" class makes a + * call to the C++ method. This is used by the generator to avoid writing calls to + * Python overrides of C++ virtual methods when the user custom code already does this. + * \param func the function to check + * \return true if the function's code snippets call the Python override for a C++ virtual method + */ + bool injectedCodeCallsPythonOverride(const AbstractMetaFunction *func); + + /** + * Verifies if any of the function's code injections attributes values to + * the return variable (%0 or %PYARG_0). + * \param func the function to check + * \param language the kind of code snip + * \return true if the function's code attributes values to "%0" or "%PYARG_0" + */ + bool injectedCodeHasReturnValueAttribution(const AbstractMetaFunction *func, TypeSystem::Language language = TypeSystem::TargetLangCode); + + /** + * Verifies if any of the function's code injections uses the type system variable + * for function arguments of a given index. + */ + bool injectedCodeUsesArgument(const AbstractMetaFunction *func, int argumentIndex); + + /** + * Function which parse the metafunction information + * \param func the function witch will be parserd + * \param option some extra options + * \param arg_count the number of function arguments + */ + QString functionSignature(const AbstractMetaFunction *func, + const QString &prepend = QString(), + const QString &append = QString(), + Options options = NoOption, + int arg_count = -1) const; + + /// Returns the top-most class that has multiple inheritance in the ancestry. + static const AbstractMetaClass *getMultipleInheritingClass(const AbstractMetaClass *metaClass); + + static bool useOverrideCaching(const AbstractMetaClass *metaClass); + AttroCheck checkAttroFunctionNeeds(const AbstractMetaClass *metaClass) const; + + /// Returns a list of methods of the given class where each one is part of a different overload with both static and non-static method. + AbstractMetaFunctionList getMethodsWithBothStaticAndNonStaticMethods(const AbstractMetaClass *metaClass); + + /// Returns a list of parent classes for a given class. + AbstractMetaClassList getBaseClasses(const AbstractMetaClass *metaClass) const; + + void writeToPythonConversion(QTextStream &s, const AbstractMetaType &type, + const AbstractMetaClass *context, const QString &argumentName); + void writeToCppConversion(QTextStream &s, const AbstractMetaType &type, const AbstractMetaClass *context, + const QString &inArgName, const QString &outArgName); + void writeToCppConversion(QTextStream &s, const AbstractMetaClass *metaClass, const QString &inArgName, const QString &outArgName); + + /// Returns true if the argument is a pointer that rejects nullptr values. + bool shouldRejectNullPointerArgument(const AbstractMetaFunction *func, int argIndex); + + /// Verifies if the class should have a C++ wrapper generated for it, instead of only a Python wrapper. + bool shouldGenerateCppWrapper(const AbstractMetaClass *metaClass) const; + + /// Condition to call WriteVirtualMethodNative. Was extracted because also used to count these calls. + bool shouldWriteVirtualMethodNative(const AbstractMetaFunction *func); + + QString wrapperName(const AbstractMetaClass *metaClass) const; + + QString fullPythonClassName(const AbstractMetaClass *metaClass); + QString fullPythonFunctionName(const AbstractMetaFunction *func); + + bool wrapperDiagnostics() const { return m_wrapperDiagnostics; } + + static QString protectedEnumSurrogateName(const AbstractMetaEnum *metaEnum); + static QString protectedFieldGetterName(const AbstractMetaField *field); + static QString protectedFieldSetterName(const AbstractMetaField *field); + + static QString pythonPrimitiveTypeName(const QString &cppTypeName); + static QString pythonPrimitiveTypeName(const PrimitiveTypeEntry *type); + + static QString pythonOperatorFunctionName(const QString &cppOpFuncName); + static QString pythonOperatorFunctionName(const AbstractMetaFunction *func); + static QString pythonRichCompareOperatorId(const QString &cppOpFuncName); + static QString pythonRichCompareOperatorId(const AbstractMetaFunction *func); + + static QString fixedCppTypeName(const CustomConversion::TargetToNativeConversion *toNative); + static QString fixedCppTypeName(const AbstractMetaType &type); + static QString fixedCppTypeName(const TypeEntry *type, QString typeName = QString()); + + static bool isNumber(const QString &cpythonApiName); + static bool isNumber(const TypeEntry *type); + static bool isNumber(const AbstractMetaType &type); + static bool isPyInt(const TypeEntry *type); + static bool isPyInt(const AbstractMetaType &type); + + /** + * Returns true if the type passed has a Python wrapper for it. + * Although namespace has a Python wrapper, it's not considered a type. + */ + static bool isWrapperType(const TypeEntry *type); + static bool isWrapperType(const ComplexTypeEntry *type); + static bool isWrapperType(const AbstractMetaType &metaType); + + /** + * Checks if the type is an Object/QObject or pointer to Value Type. + * In other words, tells if the type is "T*" and T has a Python wrapper. + */ + static bool isPointerToWrapperType(const AbstractMetaType &type); + + /** + * Returns true if \p type is an Object Type used as a value. + */ + static bool isObjectTypeUsedAsValueType(const AbstractMetaType &type); + + static bool isValueTypeWithCopyConstructorOnly(const AbstractMetaClass *metaClass); + bool isValueTypeWithCopyConstructorOnly(const TypeEntry *type) const; + bool isValueTypeWithCopyConstructorOnly(const AbstractMetaType &type) const; + + /// Returns true if the type is a primitive but not a C++ primitive. + static bool isUserPrimitive(const TypeEntry *type); + static bool isUserPrimitive(const AbstractMetaType &type); + + /// Returns true if the type is a C++ primitive, a void*, a const char*, or a std::string. + static bool isCppPrimitive(const TypeEntry *type); + static bool isCppPrimitive(const AbstractMetaType &type); + + /// Returns true if the type is a C++ integral primitive, i.e. bool, char, int, long, and their unsigned counterparts. + static bool isCppIntegralPrimitive(const TypeEntry *type); + static bool isCppIntegralPrimitive(const AbstractMetaType &type); + + /// Checks if an argument type should be dereferenced by the Python method wrapper before calling the C++ method. + static bool shouldDereferenceArgumentPointer(const AbstractMetaArgument &arg); + /// Checks if a meta type should be dereferenced by the Python method wrapper passing it to C++. + static bool shouldDereferenceAbstractMetaTypePointer(const AbstractMetaType &metaType); + + static bool visibilityModifiedToPrivate(const AbstractMetaFunction *func); + + static bool isNullPtr(const QString &value); + + QString converterObject(const AbstractMetaType &type); + QString converterObject(const TypeEntry *type); + + static QString cpythonBaseName(const AbstractMetaClass *metaClass); + static QString cpythonBaseName(const TypeEntry *type); + static QString cpythonBaseName(const AbstractMetaType &type); + static QString cpythonTypeName(const AbstractMetaClass *metaClass); + static QString cpythonTypeName(const TypeEntry *type); + QString cpythonTypeNameExt(const TypeEntry *type) const; + QString cpythonTypeNameExt(const AbstractMetaType &type) const; + QString cpythonCheckFunction(const TypeEntry *type, bool genericNumberType = false); + QString cpythonCheckFunction(AbstractMetaType metaType, bool genericNumberType = false); + /** + * Receives the argument \p type and tries to find the appropriate AbstractMetaType for it + * or a custom type check. + * \param type A string representing the type to be discovered. + * \param metaType A pointer to an AbstractMetaType pointer, to where write a new meta type object + * if one is produced from the \p type string. This object must be deallocated by + * the caller. It will set the target variable to nullptr, is \p type is a Python type. + * \return A custom check if \p type is a custom type, or an empty string if \p metaType + * receives an existing type object. + */ + QString guessCPythonCheckFunction(const QString &type, AbstractMetaType *metaType); + QString cpythonIsConvertibleFunction(const TypeEntry *type, bool genericNumberType = false, bool checkExact = false); + QString cpythonIsConvertibleFunction(AbstractMetaType metaType, bool genericNumberType = false); + QString cpythonIsConvertibleFunction(const AbstractMetaArgument &metaArg, bool genericNumberType = false); + + QString cpythonToCppConversionFunction(const AbstractMetaClass *metaClass); + QString cpythonToCppConversionFunction(const AbstractMetaType &type, const AbstractMetaClass *context = nullptr); + QString cpythonToPythonConversionFunction(const AbstractMetaType &type, const AbstractMetaClass *context = nullptr); + QString cpythonToPythonConversionFunction(const AbstractMetaClass *metaClass); + QString cpythonToPythonConversionFunction(const TypeEntry *type); + + QString cpythonFunctionName(const AbstractMetaFunction *func); + QString cpythonMethodDefinitionName(const AbstractMetaFunction *func); + QString cpythonGettersSettersDefinitionName(const AbstractMetaClass *metaClass); + static QString cpythonGetattroFunctionName(const AbstractMetaClass *metaClass); + static QString cpythonSetattroFunctionName(const AbstractMetaClass *metaClass); + static QString cpythonGetterFunctionName(const AbstractMetaField *metaField); + static QString cpythonSetterFunctionName(const AbstractMetaField *metaField); + static QString cpythonGetterFunctionName(const QPropertySpec *property, + const AbstractMetaClass *metaClass); + static QString cpythonSetterFunctionName(const QPropertySpec *property, + const AbstractMetaClass *metaClass); + QString cpythonWrapperCPtr(const AbstractMetaClass *metaClass, + const QString &argName = QLatin1String("self")) const; + QString cpythonWrapperCPtr(const AbstractMetaType &metaType, const QString &argName) const; + QString cpythonWrapperCPtr(const TypeEntry *type, const QString &argName) const; + + /// Guesses the scope to where belongs an argument's default value. + QString guessScopeForDefaultValue(const AbstractMetaFunction *func, + const AbstractMetaArgument &arg) const; + QString guessScopeForDefaultFlagsValue(const AbstractMetaFunction *func, + const AbstractMetaArgument &arg, + const QString &value) const; + + static QString cpythonEnumName(const EnumTypeEntry *enumEntry); + static QString cpythonEnumName(const AbstractMetaEnum *metaEnum); + + static QString cpythonFlagsName(const FlagsTypeEntry *flagsEntry); + static QString cpythonFlagsName(const AbstractMetaEnum *metaEnum); + /// Returns the special cast function name, the function used to proper cast class with multiple inheritance. + static QString cpythonSpecialCastFunctionName(const AbstractMetaClass *metaClass); + + QString getFormatUnitString(const AbstractMetaFunction *func, bool incRef = false) const; + + /// Returns the file name for the module global header. If no module name is provided the current will be used. + QString getModuleHeaderFileName(const QString &moduleName = QString()) const; + + OptionDescriptions options() const override; + bool handleOption(const QString &key, const QString &value) override; + + /// Returns true if the user enabled the so called "parent constructor heuristic". + bool useCtorHeuristic() const; + /// Returns true if the user enabled the so called "return value heuristic". + bool useReturnValueHeuristic() const; + /// Returns true if the generator should use the result of isNull()const to compute boolean casts. + bool useIsNullAsNbNonZero() const; + /// Returns true if the generated code should use the "#define protected public" hack. + bool avoidProtectedHack() const; + QString cppApiVariableName(const QString &moduleName = QString()) const; + QString pythonModuleObjectName(const QString &moduleName = QString()) const; + QString convertersVariableName(const QString &moduleName = QString()) const; + /** + * Returns the type index variable name for a given class. If \p alternativeTemplateName is true + * and the class is a typedef for a template class instantiation, it will return an alternative name + * made of the template class and the instantiation values, or an empty string if the class isn't + * derived from a template class at all. + */ + QString getTypeIndexVariableName(const AbstractMetaClass *metaClass, bool alternativeTemplateName = false) const; + QString getTypeIndexVariableName(const TypeEntry *type) const; + QString getTypeIndexVariableName(const AbstractMetaType &type) const; + + /// Returns true if the user don't want verbose error messages on the generated bindings. + bool verboseErrorMessagesDisabled() const; + + /** + * Builds an AbstractMetaType object from a QString. + * Returns nullptr if no type could be built from the string. + * \param typeSignature The string describing the type to be built. + * \return A new AbstractMetaType object that must be deleted by the caller, + * or a nullptr pointer in case of failure. + */ + AbstractMetaType buildAbstractMetaTypeFromString(QString typeSignature, + QString *errorMessage = nullptr); + + /// Creates an AbstractMetaType object from a TypeEntry. + AbstractMetaType buildAbstractMetaTypeFromTypeEntry(const TypeEntry *typeEntry); + /// Creates an AbstractMetaType object from an AbstractMetaClass. + AbstractMetaType buildAbstractMetaTypeFromAbstractMetaClass(const AbstractMetaClass *metaClass); + + void writeMinimalConstructorExpression(QTextStream &s, const AbstractMetaType &type, + const QString &defaultCtor = QString()); + void writeMinimalConstructorExpression(QTextStream &s, const TypeEntry *type, const QString &defaultCtor = QString()); + + void collectContainerTypesFromConverterMacros(const QString &code, bool toPythonMacro); + // verify whether the class is copyable + bool isCopyable(const AbstractMetaClass *metaClass); + + void clearTpFuncs(); + + + /// Initializes correspondences between primitive and Python types. + static void initPrimitiveTypesCorrespondences(); + /// Initializes a list of Python known type names. + static void initKnownPythonTypes(); + + void writeFunctionCall(QTextStream &s, + const AbstractMetaFunction *metaFunc, + Options options = NoOption) const; + + void writeUnusedVariableCast(QTextStream &s, const QString &variableName); + + AbstractMetaFunctionList filterFunctions(const AbstractMetaClass *metaClass); + + // All data about extended converters: the type entries of the target type, and a + // list of AbstractMetaClasses accepted as argument for the conversion. + using ExtendedConverterData = QHash<const TypeEntry *, QVector<const AbstractMetaClass *> >; + /// Returns all extended conversions for the current module. + ExtendedConverterData getExtendedConverters() const; + + /// Returns a list of converters for the non wrapper types of the current module. + QVector<const CustomConversion *> getPrimitiveCustomConversions(); + + /// Returns true if the Python wrapper for the received OverloadData must accept a list of arguments. + static bool pythonFunctionWrapperUsesListOfArguments(const OverloadData &overloadData); + + Indentor INDENT; + + const QRegularExpression &convertToCppRegEx() const + { return m_typeSystemConvRegEx[TypeSystemToCppFunction]; } + + static QString pythonArgsAt(int i); + + static QHash<QString, QString> m_pythonPrimitiveTypeName; + static QHash<QString, QString> m_pythonOperators; + static QHash<QString, QString> m_formatUnits; + static QHash<QString, QString> m_tpFuncs; + static QStringList m_knownPythonTypes; + +private: + static QString cpythonGetterFunctionName(const QString &name, + const AbstractMetaClass *enclosingClass); + static QString cpythonSetterFunctionName(const QString &name, + const AbstractMetaClass *enclosingClass); + + static const GeneratorClassInfoCacheEntry &getGeneratorClassInfo(const AbstractMetaClass *scope); + static FunctionGroups getFunctionGroupsImpl(const AbstractMetaClass *scope); + static bool classNeedsGetattroFunctionImpl(const AbstractMetaClass *metaClass); + + QString translateTypeForWrapperMethod(const AbstractMetaType &cType, + const AbstractMetaClass *context, + Options opt = NoOption) const; + + /** + * Returns all different inherited overloads of func. + * The function can be called multiple times without duplication. + * \param func the metafunction to be searched in subclasses. + * \param seen the function's minimal signatures already seen. + */ + AbstractMetaFunctionList getInheritedOverloads(const AbstractMetaFunction *func, + QSet<QString> *seen); + + /** + * Returns all overloads for a function named \p functionName. + * \param scope scope used to search for overloads. + * \param functionName the function name. + */ + AbstractMetaFunctionList getFunctionOverloads(const AbstractMetaClass *scope, + const QString &functionName); + /** + * Write a function argument in the C++ in the text stream \p s. + * This function just call \code s << argumentString(); \endcode + * \param s text stream used to write the output. + * \param func the current metafunction. + * \param argument metaargument information to be parsed. + * \param options some extra options. + */ + void writeArgument(QTextStream &s, + const AbstractMetaFunction *func, + const AbstractMetaArgument &argument, + Options options = NoOption) const; + /** + * Create a QString in the C++ format to an function argument. + * \param func the current metafunction. + * \param argument metaargument information to be parsed. + * \param options some extra options. + */ + QString argumentString(const AbstractMetaFunction *func, + const AbstractMetaArgument &argument, + Options options = NoOption) const; + + QString functionReturnType(const AbstractMetaFunction *func, Options options = NoOption) const; + + /// Utility function for writeCodeSnips. + using ArgumentVarReplacementPair = QPair<AbstractMetaArgument, QString>; + using ArgumentVarReplacementList = QVector<ArgumentVarReplacementPair>; + ArgumentVarReplacementList getArgumentReplacement(const AbstractMetaFunction* func, + bool usePyArgs, TypeSystem::Language language, + const AbstractMetaArgument *lastArg); + + /// Returns a string with the user's custom code snippets that comply with \p position and \p language. + QString getCodeSnippets(const QVector<CodeSnip> & codeSnips, + TypeSystem::CodeSnipPosition position, + TypeSystem::Language language); + + enum TypeSystemConverterVariable { + TypeSystemCheckFunction = 0, + TypeSystemIsConvertibleFunction, + TypeSystemToCppFunction, + TypeSystemToPythonFunction, + TypeSystemConverterVariables + }; + void replaceConverterTypeSystemVariable(TypeSystemConverterVariable converterVariable, QString &code); + + /// Replaces the %CONVERTTOPYTHON type system variable. + inline void replaceConvertToPythonTypeSystemVariable(QString &code) + { + replaceConverterTypeSystemVariable(TypeSystemToPythonFunction, code); + } + /// Replaces the %CONVERTTOCPP type system variable. + inline void replaceConvertToCppTypeSystemVariable(QString &code) + { + replaceConverterTypeSystemVariable(TypeSystemToCppFunction, code); + } + /// Replaces the %ISCONVERTIBLE type system variable. + inline void replaceIsConvertibleToCppTypeSystemVariable(QString &code) + { + replaceConverterTypeSystemVariable(TypeSystemIsConvertibleFunction, code); + } + /// Replaces the %CHECKTYPE type system variable. + inline void replaceTypeCheckTypeSystemVariable(QString &code) + { + replaceConverterTypeSystemVariable(TypeSystemCheckFunction, code); + } + + /// Return a prefix with '_' suitable for names in C++ + QString moduleCppPrefix(const QString &moduleName = QString()) const; + + bool m_useCtorHeuristic = false; + bool m_userReturnValueHeuristic = false; + bool m_usePySideExtensions = false; + bool m_verboseErrorMessagesDisabled = false; + bool m_useIsNullAsNbNonZero = false; + bool m_avoidProtectedHack = false; + bool m_wrapperDiagnostics = false; + + using AbstractMetaTypeCache = QHash<QString, AbstractMetaType>; + AbstractMetaTypeCache m_metaTypeFromStringCache; + + /// Type system converter variable replacement names and regular expressions. + QString m_typeSystemConvName[TypeSystemConverterVariables]; + QRegularExpression m_typeSystemConvRegEx[TypeSystemConverterVariables]; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(ShibokenGenerator::AttroCheck); + +#endif // SHIBOKENGENERATOR_H |