/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of PySide2. ** ** $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 #include "overloaddata.h" #include #include #include #include #include #include #include #include #define NULL_VALUE "NULL" #define AVOID_PROTECTED_HACK "avoid-protected-hack" #define PARENT_CTOR_HEURISTIC "enable-parent-ctor-heuristic" #define RETURN_VALUE_HEURISTIC "enable-return-value-heuristic" #define ENABLE_PYSIDE_EXTENSIONS "enable-pyside-extensions" #define DISABLE_VERBOSE_ERROR_MESSAGES "disable-verbose-error-messages" #define USE_ISNULL_AS_NB_NONZERO "use-isnull-as-nb_nonzero" //static void dumpFunction(AbstractMetaFunctionList lst); QHash ShibokenGenerator::m_pythonPrimitiveTypeName = QHash(); QHash ShibokenGenerator::m_pythonOperators = QHash(); QHash ShibokenGenerator::m_formatUnits = QHash(); QHash ShibokenGenerator::m_tpFuncs = QHash(); 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("::"), QString::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); } ShibokenGenerator::ShibokenGenerator() : Generator() { 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"); 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() { // TODO-CONVERTER: it must be caching types that were not created here. //qDeleteAll(m_metaTypeFromStringCache.values()); } void ShibokenGenerator::clearTpFuncs() { m_tpFuncs.insert(QLatin1String("__str__"), QLatin1String("0")); m_tpFuncs.insert(QLatin1String("__repr__"), QLatin1String("0")); m_tpFuncs.insert(QLatin1String("__iter__"), QLatin1String("0")); m_tpFuncs.insert(QLatin1String("__next__"), QLatin1String("0")); } 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(QLatin1String("double"), QLatin1String("PyFloat")); m_pythonPrimitiveTypeName.insert(QLatin1String("float"), 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(QLatin1String("int"), QLatin1String("i")); m_formatUnits.insert(QLatin1String("unsigned int"), QLatin1String("I")); m_formatUnits.insert(QLatin1String("short"), QLatin1String("h")); m_formatUnits.insert(QLatin1String("unsigned short"), QLatin1String("H")); m_formatUnits.insert(QLatin1String("long"), QLatin1String("l")); m_formatUnits.insert(QLatin1String("unsigned long"), QLatin1String("k")); m_formatUnits.insert(QLatin1String("long long"), QLatin1String("L")); m_formatUnits.insert(QLatin1String("__int64"), QLatin1String("L")); m_formatUnits.insert(QLatin1String("unsigned long long"), QLatin1String("K")); m_formatUnits.insert(QLatin1String("unsigned __int64"), QLatin1String("K")); m_formatUnits.insert(QLatin1String("double"), QLatin1String("d")); m_formatUnits.insert(QLatin1String("float"), 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() & AbstractMetaAttributes::FinalCppClass)) 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; else if (func->isOperatorOverload()) protectedOperators++; else protectedFunctions++; } result = result || (protectedFunctions > protectedOperators); } } else { result = result && !metaClass->hasPrivateDestructor(); } return result; } void ShibokenGenerator::lookForEnumsInClassesNotToBeGenerated(AbstractMetaEnumList& enumList, const AbstractMetaClass* metaClass) { if (!metaClass) return; if (metaClass->typeEntry()->codeGeneration() == TypeEntry::GenerateForSubclass) { const AbstractMetaEnumList &enums = metaClass->enums(); for (const AbstractMetaEnum *metaEnum : enums) { if (metaEnum->isPrivate() || metaEnum->typeEntry()->codeGeneration() == TypeEntry::GenerateForSubclass) continue; if (!enumList.contains(const_cast(metaEnum))) enumList.append(const_cast(metaEnum)); } lookForEnumsInClassesNotToBeGenerated(enumList, metaClass->enclosingClass()); } } static const AbstractMetaClass* getProperEnclosingClass(const AbstractMetaClass* metaClass) { if (!metaClass) return 0; if (metaClass->typeEntry()->codeGeneration() != TypeEntry::GenerateForSubclass) return metaClass; return getProperEnclosingClass(metaClass->enclosingClass()); } const AbstractMetaClass* ShibokenGenerator::getProperEnclosingClassForEnum(const AbstractMetaEnum* metaEnum) { return getProperEnclosingClass(metaEnum->enclosingClass()); } QString ShibokenGenerator::wrapperName(const AbstractMetaClass* metaClass) const { if (shouldGenerateCppWrapper(metaClass)) { QString result = metaClass->name(); if (metaClass->enclosingClass()) // is a inner class result.replace(QLatin1String("::"), QLatin1String("_")); result += QLatin1String("Wrapper"); return result; } else { return metaClass->qualifiedCppName(); } } QString ShibokenGenerator::wrapperName(const AbstractMetaType *metaType) const { return metaType->cppSignature(); } QString ShibokenGenerator::fullPythonFunctionName(const AbstractMetaFunction* func) { QString funcName; if (func->isOperatorOverload()) funcName = ShibokenGenerator::pythonOperatorFunctionName(func); else funcName = func->name(); if (func->ownerClass()) { QString fullName = func->ownerClass()->fullName(); if (func->isConstructor()) funcName = fullName; else funcName.prepend(fullName + 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 AbstractMetaField* metaField) { return QStringLiteral("%1_get_%2").arg(cpythonBaseName(metaField->enclosingClass()), metaField->name()); } QString ShibokenGenerator::cpythonSetterFunctionName(const AbstractMetaField* metaField) { return QStringLiteral("%1_set_%2").arg(cpythonBaseName(metaField->enclosingClass()), metaField->name()); } static QString cpythonEnumFlagsName(QString moduleName, 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 = getDefaultValue(func, arg); if (value.isEmpty()) return QString(); if (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() == QLatin1String("int")) { 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 "->")); } 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, QString argName) { return cpythonWrapperCPtr(metaClass->typeEntry(), argName); } QString ShibokenGenerator::cpythonWrapperCPtr(const AbstractMetaType *metaType, QString argName) { if (!ShibokenGenerator::isWrapperType(metaType->typeEntry())) return QString(); return QLatin1String("reinterpret_cast< ::") + metaType->cppSignature() + QLatin1String(" *>(Shiboken::Conversions::cppPointer(") + cpythonTypeNameExt(metaType) + QLatin1String(", reinterpret_cast(") + argName + QLatin1String(")))"); } QString ShibokenGenerator::cpythonWrapperCPtr(const TypeEntry* type, QString argName) { if (!ShibokenGenerator::isWrapperType(type)) return QString(); return QLatin1String("reinterpret_cast< ::") + type->qualifiedCppName() + QLatin1String(" *>(Shiboken::Conversions::cppPointer(") + cpythonTypeNameExt(type) + QLatin1String(", reinterpret_cast(") + argName + QLatin1String(")))"); } QString ShibokenGenerator::getFunctionReturnType(const AbstractMetaFunction* func, Options) const { if (func->ownerClass() && func->isConstructor()) return func->ownerClass()->qualifiedCppName() + QLatin1Char('*'); return translateTypeForWrapperMethod(func->type(), func->implementingClass()); } 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; if (!func->typeReplaced(arg->argumentIndex() + 1).isEmpty()) { result += QLatin1Char(objType); } else if (arg->type()->isQObject() || arg->type()->isObject() || arg->type()->isValue() || arg->type()->isValuePointer() || arg->type()->isNativePointer() || arg->type()->isEnum() || arg->type()->isFlags() || arg->type()->isContainer() || arg->type()->isSmartPointer() || arg->type()->referenceType() == LValueReference) { result += QLatin1Char(objType); } else if (arg->type()->isPrimitive()) { const PrimitiveTypeEntry* ptype = (const PrimitiveTypeEntry*) arg->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(arg->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 PrimitiveTypeEntry* ptype = (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((const EnumTypeEntry*) type); } else if (type->isFlags()) { baseName = cpythonFlagsName((const FlagsTypeEntry*) type); } else if (type->isContainer()) { const ContainerTypeEntry* ctype = (const ContainerTypeEntry*) type; switch (ctype->type()) { 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("_Type"); } QString ShibokenGenerator::cpythonTypeNameExt(const TypeEntry* type) { return cppApiVariableName(type->targetLangPackage()) + QLatin1Char('[') + getTypeIndexVariableName(type) + QLatin1Char(']'); } QString ShibokenGenerator::converterObject(const AbstractMetaType* type) { if (isCString(type)) return QLatin1String("Shiboken::Conversions::PrimitiveTypeConverter()"); if (isVoidPointer(type)) return QLatin1String("Shiboken::Conversions::PrimitiveTypeConverter()"); const AbstractMetaTypeCList nestedArrayTypes = type->nestedArrayTypes(); if (!nestedArrayTypes.isEmpty() && nestedArrayTypes.constLast()->isCppPrimitive()) { return QStringLiteral("Shiboken::Conversions::ArrayTypeConverter<") + nestedArrayTypes.constLast()->minimalSignature() + QLatin1String(">(") + QString::number(nestedArrayTypes.size()) + QLatin1Char(')'); } if (type->typeEntry()->isContainer()) { return convertersVariableName(type->typeEntry()->targetLangPackage()) + QLatin1Char('[') + getTypeIndexVariableName(type) + QLatin1Char(']'); } return converterObject(type->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("SBK_CONVERTER(%1)").arg(cpythonTypeNameExt(type)); if (type->isArray()) { qDebug() << "Warning: no idea how to handle the Qt5 type " << type->qualifiedCppName(); return QString::null; } /* the typedef'd primitive types case */ const PrimitiveTypeEntry* pte = dynamic_cast(type); if (!pte) { qDebug() << "Warning: the Qt5 primitive type is unknown" << type->qualifiedCppName(); return QString::null; } 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) { return cppApiVariableName(type->typeEntry()->targetLangPackage()) + QLatin1Char('[') + getTypeIndexVariableName(type) + QLatin1Char(']'); } static QString msgUnknownOperator(const AbstractMetaFunction* func) { QString result = QLatin1String("Unknown operator: \"") + func->originalName() + QLatin1Char('"'); if (const AbstractMetaClass *c = func->implementingClass()) result += QLatin1String(" in class: ") + c->name(); return result; } static inline QString unknownOperator() { return QStringLiteral("__UNKNOWN_OPERATOR__"); } QString ShibokenGenerator::cpythonOperatorFunctionName(const AbstractMetaFunction* func) { if (!func->isOperatorOverload()) return QString(); const QString pythonOp = pythonOperatorFunctionName(func->originalName()); if (pythonOp == unknownOperator()) qCWarning(lcShiboken).noquote().nospace() << msgUnknownOperator(func); return QLatin1String("Sbk") + func->ownerClass()->name() + QLatin1Char('_') + pythonOp; } 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->codeGeneration() & TypeEntry::GenerateTargetLang)) { 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(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(QString cppOpFuncName) { return QLatin1String("Py_") + m_pythonOperators.value(cppOpFuncName).toUpper(); } QString ShibokenGenerator::pythonRichCompareOperatorId(const AbstractMetaFunction* func) { return pythonRichCompareOperatorId(func->originalName()); } bool ShibokenGenerator::isNumber(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((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((const PrimitiveTypeEntry*) type) == QLatin1String("PyInt"); } bool ShibokenGenerator::isPyInt(const AbstractMetaType* type) { return isPyInt(type->typeEntry()); } bool ShibokenGenerator::isPairContainer(const AbstractMetaType* type) { return type->isContainer() && static_cast(type->typeEntry())->type() == ContainerTypeEntry::PairContainer; } bool ShibokenGenerator::isWrapperType(const TypeEntry* type) { if (type->isComplex()) return ShibokenGenerator::isWrapperType((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() & AbstractMetaAttributes::HasRejectedConstructor) != 0) return false; AbstractMetaFunctionList ctors = metaClass->queryFunctions(AbstractMetaClass::Constructors); if (ctors.count() != 1) return false; return ctors.constFirst()->functionType() == AbstractMetaFunction::CopyConstructorFunction; } 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 { if (!type || !type->typeEntry()->isValue()) return false; return isValueTypeWithCopyConstructorOnly(type->typeEntry()); } bool ShibokenGenerator::isUserPrimitive(const TypeEntry* type) { if (!type->isPrimitive()) return false; const PrimitiveTypeEntry* trueType = (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 PrimitiveTypeEntry* trueType = (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; } QString ShibokenGenerator::cpythonCheckFunction(const 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); } else if (metaType->typeEntry()->isContainer()) { QString typeCheck = QLatin1String("Shiboken::Conversions::"); ContainerTypeEntry::Type type = ((const ContainerTypeEntry*)metaType->typeEntry())->type(); 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("); 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) .arg(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(metaType->typeEntry(), genericNumberType); } QString ShibokenGenerator::cpythonCheckFunction(const TypeEntry* type, bool genericNumberType) { QString customCheck; if (type->isCustom()) { AbstractMetaType* metaType; customCheck = guessCPythonCheckFunction(type->name(), &metaType); if (metaType) return cpythonCheckFunction(metaType, genericNumberType); return customCheck; } if (type->isEnum() || type->isFlags() || isWrapperType(type)) return QString::fromLatin1("SbkObject_TypeCheck(%1, ").arg(cpythonTypeNameExt(type)); else if (isCppPrimitive(type)) return pythonPrimitiveTypeName((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 = 0; 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::guessCPythonIsConvertible(const QString& type) { if (type == QLatin1String("PyTypeObject")) return QLatin1String("PyType_Check"); AbstractMetaType* metaType = buildAbstractMetaTypeFromString(type); if (metaType && !metaType->typeEntry()->isCustom()) return cpythonIsConvertibleFunction(metaType); 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(") + cpythonTypeNameExt(type) + QLatin1String("), "); return result; } return QString::fromLatin1("Shiboken::Conversions::isPythonToCppConvertible(%1, ") .arg(converterObject(type)); } QString ShibokenGenerator::cpythonIsConvertibleFunction(const 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(") + cpythonTypeNameExt(metaType) + QLatin1String("), "); return result; } result += QLatin1String("isPythonToCppConvertible(") + converterObject(metaType); // Write out array sizes if known const AbstractMetaTypeCList 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(") + 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(") + 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(") + 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(") + 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_VALUE); //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 << " " PYTHON_SELF_VAR; } 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++; } } QString ShibokenGenerator::functionReturnType(const AbstractMetaFunction* func, Options options) const { QString modifiedReturnType = QString(func->typeReplaced(0)); if (!modifiedReturnType.isNull() && !(options & OriginalTypeDescription)) return modifiedReturnType; else return translateType(func->type(), func->implementingClass(), options); } QString ShibokenGenerator::functionSignature(const AbstractMetaFunction *func, QString prepend, 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"; return result; } void ShibokenGenerator::writeArgumentNames(QTextStream &s, const AbstractMetaFunction *func, Options options) const { AbstractMetaArgumentList arguments = func->arguments(); int argCount = 0; for (int j = 0, max = arguments.size(); j < max; j++) { if ((options & Generator::SkipRemovedArguments) && (func->argumentRemoved(arguments.at(j)->argumentIndex()+1))) continue; s << ((argCount > 0) ? ", " : "") << arguments.at(j)->name(); if (((options & Generator::VirtualCall) == 0) && (!func->conversionRule(TypeSystem::NativeCode, arguments.at(j)->argumentIndex() + 1).isEmpty() || !func->conversionRule(TypeSystem::TargetLangCode, arguments.at(j)->argumentIndex() + 1).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<< ')' << endl; } AbstractMetaFunctionList ShibokenGenerator::filterFunctions(const AbstractMetaClass* metaClass) { AbstractMetaFunctionList result; const AbstractMetaFunctionList &funcs = metaClass->functions(); for (AbstractMetaFunction *func : funcs) { if (func->isSignal() || func->isDestructor() || func->usesRValueReferences() || (func->isModifiedRemoved() && !func->isAbstract() && (!avoidProtectedHack() || !func->isProtected()))) continue; result << func; } return result; } ShibokenGenerator::ExtendedConverterData ShibokenGenerator::getExtendedConverters() const { ExtendedConverterData extConvs; const AbstractMetaClassList &classList = classes(); for (const AbstractMetaClass *metaClass : classList) { // 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->codeGeneration() & TypeEntry::GenerateTargetLang) || !convType->isValue() || convOp->isModifiedRemoved()) continue; extConvs[convType].append(convOp->ownerClass()); } } return extConvs; } QVector ShibokenGenerator::getPrimitiveCustomConversions() { QVector 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::processCodeSnip(QString& code, const AbstractMetaClass* context) { if (context) { // Replace template variable by the Python Type object // for the class context in which the variable is used. code.replace(QLatin1String("%PYTHONTYPEOBJECT"), cpythonTypeName(context) + QLatin1String(".super.ht_type")); code.replace(QLatin1String("%TYPE"), wrapperName(context)); code.replace(QLatin1String("%CPPTYPE"), context->name()); } // 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(); bool argRemoved = func->argumentRemoved(i+1); removed = removed + (int) argRemoved; 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; const 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 ? QString::fromLatin1(PYTHON_ARGS "[%1]").arg(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::writeCodeSnips(QTextStream& s, const CodeSnipList& codeSnips, TypeSystem::CodeSnipPosition position, TypeSystem::Language language, const AbstractMetaClass* context) { QString code = getCodeSnippets(codeSnips, position, language); if (code.isEmpty()) return; processCodeSnip(code, context); s << INDENT << "// Begin code injection" << endl; s << code; s << INDENT << "// End of code injection" << endl; } static QString msgWrongIndex(const char *varName, const QString &capture, const AbstractMetaFunction *func) { QString result; QTextStream str(&result); str << "Wrong index for " << varName << " variable (" << capture << ") on "; if (const AbstractMetaClass *c = func->implementingClass()) str << c->name() << "::"; str << func->signature(); return result; } 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++; } OverloadData od(getFunctionGroups(func->implementingClass())[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"[\\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(" PYTHON_ARGS ", \\1-1, \\2)")); code.replace(pyArgsRegex, QLatin1String("PyTuple_GET_ITEM(" PYTHON_ARGS ", \\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->type()) { 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(PYTHON_SELF_VAR); 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.isNull()) { 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(%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(%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(".super.ht_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" << endl; s << code; s << INDENT << "// End of code injection" << endl; } // 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; } typedef QPair StringPair; void ShibokenGenerator::replaceConverterTypeSystemVariable(TypeSystemConverterVariable converterVariable, QString& code) { QVector 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(); QString conversionTypeName = list.constLast(); const AbstractMetaType* conversionType = buildAbstractMetaTypeFromString(conversionTypeName); if (!conversionType) { qFatal(qPrintable(QString::fromLatin1("Could not find type '%1' for use in '%2' conversion. " "Make sure to use the full C++ name, e.g. 'Namespace::Class'.") .arg(conversionTypeName).arg(m_typeSystemConvName[converterVariable])), NULL); } 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()) { if (varType != conversionType->cppSignature()) { qFatal(qPrintable(QString::fromLatin1("Types of receiver variable ('%1') and %CONVERTTOCPP type system variable ('%2') differ.") .arg(varType, conversionType->cppSignature())), NULL); } c << getFullTypeName(conversionType) << ' ' << varName; writeMinimalConstructorExpression(c, conversionType); c << ';' << endl; 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; } case TypeSystemIsConvertibleFunction: if (conversion.isEmpty()) conversion = cpythonIsConvertibleFunction(conversionType); case TypeSystemToPythonFunction: if (conversion.isEmpty()) conversion = cpythonToPythonConversionFunction(conversionType); default: { QString arg = getConverterTypeSystemVariableArgument(code, match.capturedEnd()); conversionString += arg; if (converterVariable == TypeSystemToPythonFunction && !isVariable(arg)) { qFatal(qPrintable(QString::fromLatin1("Only variables are acceptable as argument to %%CONVERTTOPYTHON type system variable on code snippet: '%1'") .arg(code)), NULL); } 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::injectedCodeUsesCppSelf(const AbstractMetaFunction* func) { CodeSnipList snips = func->injectedCodeSnips(TypeSystem::CodeSnipPositionAny, TypeSystem::TargetLangCode); for (const CodeSnip &snip : qAsConst(snips)) { if (snip.code().contains(QLatin1String("%CPPSELF"))) return true; } return false; } 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 AbstractMetaFunction* func) { QString funcCall = func->originalName() + QLatin1Char('('); QString wrappedCtorCall; if (func->isConstructor()) { funcCall.prepend(QLatin1String("new ")); wrappedCtorCall = QStringLiteral("new %1(").arg(wrapperName(func->ownerClass())); } 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::hasMultipleInheritanceInAncestry(const AbstractMetaClass* metaClass) { if (!metaClass || metaClass->baseClassNames().isEmpty()) return false; if (metaClass->baseClassNames().size() > 1) return true; return hasMultipleInheritanceInAncestry(metaClass->baseClass()); } typedef QMap FunctionGroupMap; typedef FunctionGroupMap::const_iterator FunctionGroupMapIt; bool ShibokenGenerator::classNeedsGetattroFunction(const AbstractMetaClass* metaClass) { if (!metaClass) return false; if (metaClass->typeEntry()->isSmartPointer()) return true; const FunctionGroupMap &functionGroup = getFunctionGroups(metaClass); for (FunctionGroupMapIt 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; } bool ShibokenGenerator::classNeedsSetattroFunction(const AbstractMetaClass *metaClass) { if (!metaClass) return false; if (metaClass->typeEntry()->isSmartPointer()) return true; return false; } AbstractMetaFunctionList ShibokenGenerator::getMethodsWithBothStaticAndNonStaticMethods(const AbstractMetaClass* metaClass) { AbstractMetaFunctionList methods; if (metaClass) { const FunctionGroupMap &functionGroups = getFunctionGroups(metaClass); for (FunctionGroupMapIt 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 0; 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 { QString result = moduleName.isEmpty() ? packageName() : moduleName; result.replace(QLatin1Char('.'), QLatin1Char('_')); return result.toLower() + QLatin1String("_python.h"); } QString ShibokenGenerator::extendedIsConvertibleFunctionName(const TypeEntry* targetType) const { QString p = targetType->targetLangPackage(); p.replace(QLatin1Char('.'), QLatin1Char('_')); return QStringLiteral("ExtendedIsConvertible_%1_%2").arg(p, targetType->name()); } QString ShibokenGenerator::extendedToCppFunctionName(const TypeEntry* targetType) const { QString p = targetType->targetLangPackage(); p.replace(QLatin1Char('.'), QLatin1Char('_')); return QStringLiteral("ExtendedToCpp_%1_%2").arg(p, targetType->name()); } bool ShibokenGenerator::isCopyable(const AbstractMetaClass *metaClass) { if (metaClass->isNamespace() || isObjectType(metaClass)) return false; else if (metaClass->typeEntry()->copyable() == ComplexTypeEntry::Unknown) return metaClass->hasCloneOperator(); else return (metaClass->typeEntry()->copyable() == ComplexTypeEntry::CopyableSet); return false; } AbstractMetaType* ShibokenGenerator::buildAbstractMetaTypeFromString(QString typeSignature) { typeSignature = typeSignature.trimmed(); if (typeSignature.startsWith(QLatin1String("::"))) typeSignature.remove(0, 2); if (m_metaTypeFromStringCache.contains(typeSignature)) return m_metaTypeFromStringCache.value(typeSignature); QString typeString = typeSignature; bool isConst = typeString.startsWith(QLatin1String("const ")); if (isConst) typeString.remove(0, sizeof("const ") / sizeof(char) - 1); ReferenceType refType = NoReference; if (typeString.endsWith(QLatin1String("&&"))) { refType = RValueReference; typeString.chop(2); typeString = typeString.trimmed(); } else if (typeString.endsWith(QLatin1Char('&'))) { refType = LValueReference; typeString.chop(1); typeString = typeString.trimmed(); } int indirections = 0; while (typeString.endsWith(QLatin1Char('*'))) { ++indirections; typeString.chop(1); typeString = typeString.trimmed(); } if (typeString.startsWith(QLatin1String("::"))) typeString.remove(0, 2); QString adjustedTypeName = typeString; QStringList instantiatedTypes; int lpos = typeString.indexOf(QLatin1Char('<')); if (lpos > -1) { int rpos = typeString.lastIndexOf(QLatin1Char('>')); if ((lpos != -1) && (rpos != -1)) { QString type = typeString.mid(lpos + 1, rpos - lpos - 1); int depth = 0; int start = 0; for (int i = 0; i < type.count(); ++i) { if (type.at(i) == QLatin1Char('<')) { ++depth; } else if (type.at(i) == QLatin1Char('>')) { --depth; } else if (type.at(i) == QLatin1Char(',') && depth == 0) { instantiatedTypes << type.mid(start, i - start).trimmed(); start = i + 1; } } instantiatedTypes << type.mid(start).trimmed(); adjustedTypeName.truncate(lpos); } } TypeEntry* typeEntry = TypeDatabase::instance()->findType(adjustedTypeName); AbstractMetaType* metaType = 0; if (typeEntry) { metaType = new AbstractMetaType(); metaType->setTypeEntry(typeEntry); metaType->setIndirections(indirections); metaType->setReferenceType(refType); metaType->setConstant(isConst); metaType->setTypeUsagePattern(AbstractMetaType::ContainerPattern); for (const QString &instantiation : qAsConst(instantiatedTypes)) { AbstractMetaType* tmplArgType = buildAbstractMetaTypeFromString(instantiation); metaType->addInstantiation(tmplArgType); } metaType->decideUsagePattern(); m_metaTypeFromStringCache.insert(typeSignature, metaType); } return metaType; } 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 = new AbstractMetaType; metaType->setTypeEntry(typeEntry); metaType->setIndirections(0); 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) { if (func->isSignal() || func->isDestructor() || (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; } QMap< QString, AbstractMetaFunctionList > ShibokenGenerator::getFunctionGroups(const AbstractMetaClass* scope) { AbstractMetaFunctionList lst = scope ? scope->functions() : globalFunctions(); QMap results; for (AbstractMetaFunction *func : qAsConst(lst)) { if (isGroupable(func)) results[func->name()].append(func); } return results; } AbstractMetaFunctionList ShibokenGenerator::getInheritedOverloads(const AbstractMetaFunction *func, QSet *seen) { AbstractMetaFunctionList results; AbstractMetaClass* basis; if (func->ownerClass() && (basis = func->ownerClass()->baseClass(), basis)) { 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 *seen) { AbstractMetaFunctionList results; seen->insert(func->minimalSignature()); results << const_cast(func) << getInheritedOverloads(func, seen); return results; } AbstractMetaFunctionList ShibokenGenerator::getFunctionOverloads(const AbstractMetaClass* scope, const QString& functionName) { AbstractMetaFunctionList lst = scope ? scope->functions() : globalFunctions(); AbstractMetaFunctionList results; QSet 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; } QPair< int, int > ShibokenGenerator::getMinMaxArguments(const AbstractMetaFunction* metaFunction) { AbstractMetaFunctionList overloads = getFunctionOverloads(metaFunction->ownerClass(), metaFunction->name()); int minArgs = std::numeric_limits::max(); int maxArgs = 0; for (const AbstractMetaFunction* func : qAsConst(overloads)) { int numArgs = 0; const AbstractMetaArgumentList &arguments = func->arguments(); for (const AbstractMetaArgument *arg : arguments) { if (!func->argumentRemoved(arg->argumentIndex() + 1)) numArgs++; } maxArgs = std::max(maxArgs, numArgs); minArgs = std::min(minArgs, numArgs); } return qMakePair(minArgs, maxArgs); } 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")); } 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(const QMap& args) { m_useCtorHeuristic = args.contains(QLatin1String(PARENT_CTOR_HEURISTIC)); m_usePySideExtensions = args.contains(QLatin1String(ENABLE_PYSIDE_EXTENSIONS)); m_userReturnValueHeuristic = args.contains(QLatin1String(RETURN_VALUE_HEURISTIC)); m_verboseErrorMessagesDisabled = args.contains(QLatin1String(DISABLE_VERBOSE_ERROR_MESSAGES)); m_useIsNullAsNbNonZero = args.contains(QLatin1String(USE_ISNULL_AS_NB_NONZERO)); m_avoidProtectedHack = args.contains(QLatin1String(AVOID_PROTECTED_HACK)); TypeDatabase* td = TypeDatabase::instance(); 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); const AbstractMetaClassList &classList = classes(); for (const AbstractMetaClass *metaClass : classList) getCode(snips, metaClass->typeEntry()); getCode(snips, td->findType(packageName())); const FunctionGroupMap &functionGroups = getFunctionGroups(); for (FunctionGroupMapIt 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; 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); AbstractMetaType* type = buildAbstractMetaTypeFromString(typeString); addInstantiatedContainersAndSmartPointers(type, type->originalTypeDescription()); } 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::cppApiVariableName(const QString& moduleName) const { QString result = moduleName.isEmpty() ? ShibokenGenerator::packageName() : moduleName; result.replace(QLatin1Char('.'), QLatin1Char('_')); result.prepend(QLatin1String("Sbk")); result.append(QLatin1String("Types")); return result; } 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(); const AbstractMetaTypeList &instantiations = type->instantiations(); for (const AbstractMetaType *instantiation : instantiations) { res += instantiation->isContainer() ? processInstantiationsVariableName(instantiation) : QLatin1Char('_') + _fixedCppTypeName(instantiation->cppSignature()).toUpper(); } return res; } QString ShibokenGenerator::getTypeIndexVariableName(const AbstractMetaClass* metaClass, bool alternativeTemplateName) { if (alternativeTemplateName) { const AbstractMetaClass* templateBaseClass = metaClass->templateBaseClass(); if (!templateBaseClass) return QString(); QString base = _fixedCppTypeName(templateBaseClass->typeEntry()->qualifiedCppName()).toUpper(); QString instantiations; const AbstractMetaTypeList &templateBaseClassInstantiations = metaClass->templateBaseClassInstantiations(); for (const AbstractMetaType *instantiation : templateBaseClassInstantiations) instantiations += processInstantiationsVariableName(instantiation); return QString::fromLatin1("SBK_%1%2_IDX").arg(base, instantiations); } return getTypeIndexVariableName(metaClass->typeEntry()); } QString ShibokenGenerator::getTypeIndexVariableName(const TypeEntry* type) { if (type->isCppPrimitive()) { const PrimitiveTypeEntry* trueType = (const PrimitiveTypeEntry*) type; if (trueType->basicReferencedTypeEntry()) type = trueType->basicReferencedTypeEntry(); } return QString::fromLatin1("SBK_%1_IDX").arg(_fixedCppTypeName(type->qualifiedCppName()).toUpper()); } QString ShibokenGenerator::getTypeIndexVariableName(const AbstractMetaType* type) { return QString::fromLatin1("SBK%1%2_IDX") .arg(type->typeEntry()->isContainer() ? QLatin1Char('_') + moduleName().toUpper() : QString(), processInstantiationsVariableName(type)); } 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(); } Generator::Options ShibokenGenerator::getConverterOptions(const AbstractMetaType* metaType) { // exclude const on Objects Options flags; const TypeEntry* type = metaType->typeEntry(); bool isCStr = isCString(metaType); if (metaType->indirections() && !isCStr) { flags = ExcludeConst; } else if (metaType->isContainer() || (type->isPrimitive() && !isCStr) // const refs become just the value, but pure refs must remain pure. || (type->isValue() && metaType->isConstant() && metaType->referenceType() == LValueReference)) { flags = ExcludeConst | ExcludeReference; } return flags; } QString ShibokenGenerator::getDefaultValue(const AbstractMetaFunction* func, const AbstractMetaArgument* arg) { if (!arg->defaultValueExpression().isEmpty()) return arg->defaultValueExpression(); //Check modifications const FunctionModificationList &mods = func->modifications(); for (const FunctionModification &m : mods) { for (const ArgumentModification &am : m.argument_mods) { if (am.index == (arg->argumentIndex() + 1)) return am.replacedDefaultExpression; } } return QString(); } void ShibokenGenerator::writeMinimalConstructorExpression(QTextStream& s, const AbstractMetaType* type, const QString& defaultCtor) { if (defaultCtor.isEmpty() && isCppPrimitive(type)) return; QString ctor = defaultCtor.isEmpty() ? minimalConstructor(type) : defaultCtor; if (ctor.isEmpty()) { const QString message = msgCouldNotFindMinimalConstructor(QLatin1String(__FUNCTION__), type->cppSignature()); qCWarning(lcShiboken()).noquote() << message; s << ";\n#error " << message << '\n'; } else { s << " = " << ctor; } } void ShibokenGenerator::writeMinimalConstructorExpression(QTextStream& s, const TypeEntry* type, const QString& defaultCtor) { if (defaultCtor.isEmpty() && isCppPrimitive(type)) return; QString ctor = defaultCtor.isEmpty() ? minimalConstructor(type) : defaultCtor; if (ctor.isEmpty()) { const QString message = msgCouldNotFindMinimalConstructor(QLatin1String(__FUNCTION__), type->qualifiedCppName()); qCWarning(lcShiboken()).noquote() << message; s << ";\n#error " << message << endl; } else { s << " = " << ctor; } } bool ShibokenGenerator::isCppIntegralPrimitive(const TypeEntry* type) { if (!type->isCppPrimitive()) return false; const PrimitiveTypeEntry* trueType = (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::msgCouldNotFindMinimalConstructor(const QString &where, const QString &type) { return where + QLatin1String(": Could not find a minimal constructor for type '") + type + QLatin1String("'. This will result in a compilation error."); }