From bd2df4ea6fd11f1285c07591367259f3900a93fc Mon Sep 17 00:00:00 2001 From: Hugo Parente Lima Date: Tue, 28 Feb 2012 23:44:50 -0300 Subject: generators directory renamed to generator. This makes sense as now there only one generator that you can tweak it into different modes (a.k.a. generator sets). Reviewer: Trust me --- generator/CMakeLists.txt | 32 + generator/generator.cpp | 708 +++++ generator/generator.h | 341 ++ generator/main.cpp | 443 +++ generator/qtdoc/CMakeLists.txt | 21 + generator/qtdoc/qtdocgenerator.cpp | 1652 ++++++++++ generator/qtdoc/qtdocgenerator.h | 226 ++ generator/shiboken/CMakeLists.txt | 30 + generator/shiboken/cppgenerator.cpp | 5125 ++++++++++++++++++++++++++++++ generator/shiboken/cppgenerator.h | 334 ++ generator/shiboken/headergenerator.cpp | 494 +++ generator/shiboken/headergenerator.h | 56 + generator/shiboken/overloaddata.cpp | 1002 ++++++ generator/shiboken/overloaddata.h | 147 + generator/shiboken/shibokengenerator.cpp | 2466 ++++++++++++++ generator/shiboken/shibokengenerator.h | 536 ++++ generator/shiboken/shibokennormalize.cpp | 274 ++ generator/shiboken/shibokennormalize_p.h | 41 + generator/shibokenconfig.h.in | 7 + 19 files changed, 13935 insertions(+) create mode 100644 generator/CMakeLists.txt create mode 100644 generator/generator.cpp create mode 100644 generator/generator.h create mode 100644 generator/main.cpp create mode 100644 generator/qtdoc/CMakeLists.txt create mode 100644 generator/qtdoc/qtdocgenerator.cpp create mode 100644 generator/qtdoc/qtdocgenerator.h create mode 100644 generator/shiboken/CMakeLists.txt create mode 100644 generator/shiboken/cppgenerator.cpp create mode 100644 generator/shiboken/cppgenerator.h create mode 100644 generator/shiboken/headergenerator.cpp create mode 100644 generator/shiboken/headergenerator.h create mode 100644 generator/shiboken/overloaddata.cpp create mode 100644 generator/shiboken/overloaddata.h create mode 100644 generator/shiboken/shibokengenerator.cpp create mode 100644 generator/shiboken/shibokengenerator.h create mode 100644 generator/shiboken/shibokennormalize.cpp create mode 100644 generator/shiboken/shibokennormalize_p.h create mode 100644 generator/shibokenconfig.h.in (limited to 'generator') diff --git a/generator/CMakeLists.txt b/generator/CMakeLists.txt new file mode 100644 index 000000000..37a6f3cc9 --- /dev/null +++ b/generator/CMakeLists.txt @@ -0,0 +1,32 @@ +project(shibokengenerator) + +set(shiboken_SRC +generator.cpp +shiboken/cppgenerator.cpp +shiboken/headergenerator.cpp +shiboken/overloaddata.cpp +shiboken/shibokengenerator.cpp +shiboken/shibokennormalize.cpp +qtdoc/qtdocgenerator.cpp +main.cpp +) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/shiboken + ${CMAKE_CURRENT_SOURCE_DIR}/qtdoc + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} + ${APIEXTRACTOR_INCLUDE_DIR} + ${QT_INCLUDE_DIR} + ${QT_QTCORE_INCLUDE_DIR} + ${QT_QTXML_INCLUDE_DIR}) + +add_executable(shiboken ${shiboken_SRC}) +set_target_properties(shiboken PROPERTIES OUTPUT_NAME shiboken${shiboken_SUFFIX}) +target_link_libraries(shiboken + ${APIEXTRACTOR_LIBRARY} + ${QT_QTCORE_LIBRARY} + ${QT_QTXML_LIBRARY}) + +configure_file(shibokenconfig.h.in "${CMAKE_CURRENT_BINARY_DIR}/shibokenconfig.h" @ONLY) + +install(TARGETS shiboken DESTINATION bin) diff --git a/generator/generator.cpp b/generator/generator.cpp new file mode 100644 index 000000000..9f81b93ad --- /dev/null +++ b/generator/generator.cpp @@ -0,0 +1,708 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "generator.h" +#include "reporthandler.h" +#include "fileout.h" +#include "apiextractor.h" + +#include +#include +#include +#include +#include + +struct Generator::GeneratorPrivate { + const ApiExtractor* apiextractor; + QString outDir; + // License comment + QString licenseComment; + QString packageName; + int numGenerated; + int numGeneratedWritten; + QStringList instantiatedContainersNames; + QList instantiatedContainers; +}; + +Generator::Generator() : m_d(new GeneratorPrivate) +{ + m_d->numGenerated = 0; + m_d->numGeneratedWritten = 0; + m_d->instantiatedContainers = QList(); + m_d->instantiatedContainersNames = QStringList(); +} + +Generator::~Generator() +{ + delete m_d; +} + +bool Generator::setup(const ApiExtractor& extractor, const QMap< QString, QString > args) +{ + m_d->apiextractor = &extractor; + TypeEntryHash allEntries = TypeDatabase::instance()->allEntries(); + TypeEntry* entryFound = 0; + foreach (QList entryList, allEntries.values()) { + foreach (TypeEntry* entry, entryList) { + if (entry->type() == TypeEntry::TypeSystemType && entry->generateCode()) { + entryFound = entry; + break; + } + } + if (entryFound) + break; + } + if (entryFound) + m_d->packageName = entryFound->name(); + else + ReportHandler::warning("Couldn't find the package name!!"); + + collectInstantiatedContainers(); + + return doSetup(args); +} + +QString Generator::getSimplifiedContainerTypeName(const AbstractMetaType* type) +{ + if (!type->typeEntry()->isContainer()) + return type->cppSignature(); + QString typeName = type->cppSignature(); + if (type->isConstant()) + typeName.remove(0, sizeof("const ") / sizeof(char) - 1); + if (type->isReference()) + typeName.chop(1); + while (typeName.endsWith('*') || typeName.endsWith(' ')) + typeName.chop(1); + return typeName; +} + +void Generator::addInstantiatedContainers(const AbstractMetaType* type) +{ + if (!type) + return; + foreach (const AbstractMetaType* t, type->instantiations()) + addInstantiatedContainers(t); + if (!type->typeEntry()->isContainer()) + return; + QString typeName = getSimplifiedContainerTypeName(type); + if (!m_d->instantiatedContainersNames.contains(typeName)) { + m_d->instantiatedContainersNames.append(typeName); + m_d->instantiatedContainers.append(type); + } +} + +void Generator::collectInstantiatedContainers(const AbstractMetaFunction* func) +{ + addInstantiatedContainers(func->type()); + foreach (const AbstractMetaArgument* arg, func->arguments()) + addInstantiatedContainers(arg->type()); +} + +void Generator::collectInstantiatedContainers(const AbstractMetaClass* metaClass) +{ + if (!metaClass->typeEntry()->generateCode()) + return; + foreach (const AbstractMetaFunction* func, metaClass->functions()) + collectInstantiatedContainers(func); + foreach (const AbstractMetaField* field, metaClass->fields()) + addInstantiatedContainers(field->type()); + foreach (AbstractMetaClass* innerClass, metaClass->innerClasses()) + collectInstantiatedContainers(innerClass); +} + +void Generator::collectInstantiatedContainers() +{ + foreach (const AbstractMetaFunction* func, globalFunctions()) + collectInstantiatedContainers(func); + foreach (const AbstractMetaClass* metaClass, classes()) + collectInstantiatedContainers(metaClass); +} + +QList Generator::instantiatedContainers() const +{ + return m_d->instantiatedContainers; +} + +QMap< QString, QString > Generator::options() const +{ + return QMap(); +} + +AbstractMetaClassList Generator::classes() const +{ + return m_d->apiextractor->classes(); +} + +AbstractMetaFunctionList Generator::globalFunctions() const +{ + return m_d->apiextractor->globalFunctions(); +} + +AbstractMetaEnumList Generator::globalEnums() const +{ + return m_d->apiextractor->globalEnums(); +} + +QList Generator::primitiveTypes() const +{ + return m_d->apiextractor->primitiveTypes(); +} + +QList Generator::containerTypes() const +{ + return m_d->apiextractor->containerTypes(); +} + +const AbstractMetaEnum* Generator::findAbstractMetaEnum(const EnumTypeEntry* typeEntry) const +{ + return m_d->apiextractor->findAbstractMetaEnum(typeEntry); +} + +const AbstractMetaEnum* Generator::findAbstractMetaEnum(const TypeEntry* typeEntry) const +{ + return m_d->apiextractor->findAbstractMetaEnum(typeEntry); +} + +const AbstractMetaEnum* Generator::findAbstractMetaEnum(const FlagsTypeEntry* typeEntry) const +{ + return m_d->apiextractor->findAbstractMetaEnum(typeEntry); +} + +const AbstractMetaEnum* Generator::findAbstractMetaEnum(const AbstractMetaType* metaType) const +{ + return m_d->apiextractor->findAbstractMetaEnum(metaType); +} + +QSet< QString > Generator::qtMetaTypeDeclaredTypeNames() const +{ + return m_d->apiextractor->qtMetaTypeDeclaredTypeNames(); +} + +QString Generator::licenseComment() const +{ + return m_d->licenseComment; +} + +void Generator::setLicenseComment(const QString& licenseComment) +{ + m_d->licenseComment = licenseComment; +} + +QString Generator::packageName() const +{ + return m_d->packageName; +} + +QString Generator::moduleName() const +{ + QString& pkgName = m_d->packageName; + return QString(pkgName).remove(0, pkgName.lastIndexOf('.') + 1); +} + +QString Generator::outputDirectory() const +{ + return m_d->outDir; +} + +void Generator::setOutputDirectory(const QString &outDir) +{ + m_d->outDir = outDir; +} + +int Generator::numGenerated() const +{ + return m_d->numGenerated; +} + +int Generator::numGeneratedAndWritten() const +{ + return m_d->numGeneratedWritten; +} + +void Generator::generate() +{ + foreach (AbstractMetaClass *cls, m_d->apiextractor->classes()) { + if (!shouldGenerate(cls)) + continue; + + QString fileName = fileNameForClass(cls); + if (fileName.isNull()) + continue; + ReportHandler::debugSparse(QString("generating: %1").arg(fileName)); + + FileOut fileOut(outputDirectory() + '/' + subDirectoryForClass(cls) + '/' + fileName); + generateClass(fileOut.stream, cls); + + if (fileOut.done()) + ++m_d->numGeneratedWritten; + ++m_d->numGenerated; + } + finishGeneration(); +} + +bool Generator::shouldGenerateTypeEntry(const TypeEntry* type) const +{ + return type->codeGeneration() & TypeEntry::GenerateTargetLang; +} + +bool Generator::shouldGenerate(const AbstractMetaClass* metaClass) const +{ + return shouldGenerateTypeEntry(metaClass->typeEntry()); +} + +void verifyDirectoryFor(const QFile &file) +{ + QDir dir = QFileInfo(file).dir(); + if (!dir.exists()) { + if (!dir.mkpath(dir.absolutePath())) + ReportHandler::warning(QString("unable to create directory '%1'") + .arg(dir.absolutePath())); + } +} + +void Generator::replaceTemplateVariables(QString &code, const AbstractMetaFunction *func) +{ + const AbstractMetaClass *cpp_class = func->ownerClass(); + if (cpp_class) + code.replace("%TYPE", cpp_class->name()); + + foreach (AbstractMetaArgument *arg, func->arguments()) + code.replace("%" + QString::number(arg->argumentIndex() + 1), arg->name()); + + //template values + code.replace("%RETURN_TYPE", translateType(func->type(), cpp_class)); + code.replace("%FUNCTION_NAME", func->originalName()); + + if (code.contains("%ARGUMENT_NAMES")) { + QString str; + QTextStream aux_stream(&str); + writeArgumentNames(aux_stream, func, Generator::SkipRemovedArguments); + code.replace("%ARGUMENT_NAMES", str); + } + + if (code.contains("%ARGUMENTS")) { + QString str; + QTextStream aux_stream(&str); + writeFunctionArguments(aux_stream, func, Options(SkipDefaultValues) | SkipRemovedArguments); + code.replace("%ARGUMENTS", str); + } +} + +QTextStream& formatCode(QTextStream &s, const QString& code, Indentor &indentor) +{ + // detect number of spaces before the first character + QStringList lst(code.split("\n")); + QRegExp nonSpaceRegex("[^\\s]"); + int spacesToRemove = 0; + foreach(QString line, lst) { + if (!line.trimmed().isEmpty()) { + spacesToRemove = line.indexOf(nonSpaceRegex); + if (spacesToRemove == -1) + spacesToRemove = 0; + break; + } + } + + static QRegExp emptyLine("\\s*[\\r]?[\\n]?\\s*"); + + foreach(QString line, lst) { + if (!line.isEmpty() && !emptyLine.exactMatch(line)) { + while (line.end()->isSpace()) + line.chop(1); + int limit = 0; + for(int i = 0; i < spacesToRemove; ++i) { + if (!line[i].isSpace()) + break; + limit++; + } + + s << indentor << line.remove(0, limit); + } + s << endl; + } + return s; +} + +AbstractMetaFunctionList Generator::implicitConversions(const TypeEntry* type) const +{ + if (type->isValue()) { + const AbstractMetaClass* metaClass = classes().findClass(type); + if (metaClass) + return metaClass->implicitConversions(); + } + return AbstractMetaFunctionList(); +} + +AbstractMetaFunctionList Generator::implicitConversions(const AbstractMetaType* metaType) const +{ + return implicitConversions(metaType->typeEntry()); +} + +bool Generator::isObjectType(const TypeEntry* type) +{ + if (type->isComplex()) + return Generator::isObjectType((const ComplexTypeEntry*)type); + return type->isObject(); +} +bool Generator::isObjectType(const ComplexTypeEntry* type) +{ + return type->isObject() || type->isQObject(); +} +bool Generator::isObjectType(const AbstractMetaClass* metaClass) +{ + return Generator::isObjectType(metaClass->typeEntry()); +} +bool Generator::isObjectType(const AbstractMetaType* metaType) +{ + return isObjectType(metaType->typeEntry()); +} + +bool Generator::isPointer(const AbstractMetaType* type) +{ + return type->indirections() > 0 + || type->isNativePointer() + || type->isValuePointer(); +} + +bool Generator::isCString(const AbstractMetaType* type) +{ + return type->isNativePointer() + && type->indirections() == 1 + && type->name() == "char"; +} + +bool Generator::isVoidPointer(const AbstractMetaType* type) +{ + return type->isNativePointer() + && type->indirections() == 1 + && type->name() == "void"; +} + +QString Generator::getFullTypeName(const TypeEntry* type) const +{ + return QString("%1%2").arg(type->isCppPrimitive() ? "" : "::").arg(type->qualifiedCppName()); +} + +QString Generator::getFullTypeName(const AbstractMetaType* type) const +{ + if (isCString(type)) + return "const char*"; + if (isVoidPointer(type)) + return "void*"; + if (type->typeEntry()->isContainer()) + return QString("::%1").arg(type->cppSignature()); + QString typeName; + if (type->typeEntry()->isComplex() && type->hasInstantiations()) + typeName = getFullTypeNameWithoutModifiers(type); + else + typeName = getFullTypeName(type->typeEntry()); + return typeName + QString("*").repeated(type->indirections()); +} + +QString Generator::getFullTypeName(const AbstractMetaClass* metaClass) const +{ + return QString("::%1").arg(metaClass->qualifiedCppName()); +} + +QString Generator::getFullTypeNameWithoutModifiers(const AbstractMetaType* type) const +{ + if (isCString(type)) + return "const char*"; + if (isVoidPointer(type)) + return "void*"; + if (!type->hasInstantiations()) + return getFullTypeName(type->typeEntry()); + QString typeName = type->cppSignature(); + if (type->isConstant()) + typeName.remove(0, sizeof("const ") / sizeof(char) - 1); + if (type->isReference()) + typeName.chop(1); + while (typeName.endsWith('*') || typeName.endsWith(' ')) + typeName.chop(1); + return QString("::%1").arg(typeName); +} + +QString Generator::minimalConstructor(const AbstractMetaType* type) const +{ + if (!type || (type->isReference() && Generator::isObjectType(type))) + return QString(); + + if (type->isContainer()) { + QString ctor = type->cppSignature(); + if (ctor.endsWith("*")) + return QString("0"); + if (ctor.startsWith("const ")) + ctor.remove(0, sizeof("const ") / sizeof(char) - 1); + if (ctor.endsWith("&")) { + ctor.chop(1); + ctor = ctor.trimmed(); + } + return QString("::%1()").arg(ctor); + } + + if (type->isNativePointer()) + return QString("((%1*)0)").arg(type->typeEntry()->qualifiedCppName()); + + if (Generator::isPointer(type)) + return QString("((::%1*)0)").arg(type->typeEntry()->qualifiedCppName()); + + if (type->typeEntry()->isComplex()) { + const ComplexTypeEntry* cType = reinterpret_cast(type->typeEntry()); + QString ctor = cType->defaultConstructor(); + if (!ctor.isEmpty()) + return ctor; + ctor = minimalConstructor(classes().findClass(cType)); + if (type->hasInstantiations()) + ctor = ctor.replace(getFullTypeName(cType), getFullTypeNameWithoutModifiers(type)); + return ctor; + } + + return minimalConstructor(type->typeEntry()); +} + +QString Generator::minimalConstructor(const TypeEntry* type) const +{ + if (!type) + return QString(); + + if (type->isCppPrimitive()) + return QString("((%1)0)").arg(type->qualifiedCppName()); + + if (type->isEnum() || type->isFlags()) + return QString("((::%1)0)").arg(type->qualifiedCppName()); + + if (type->isPrimitive()) { + QString ctor = reinterpret_cast(type)->defaultConstructor(); + // If a non-C++ (i.e. defined by the user) primitive type does not have + // a default constructor defined by the user, the empty constructor is + // heuristically returned. If this is wrong the build of the generated + // bindings will tell. + return (ctor.isEmpty()) ? QString("::%1()").arg(type->qualifiedCppName()) : ctor; + } + + if (type->isComplex()) + return minimalConstructor(classes().findClass(type)); + + return QString(); +} + +QString Generator::minimalConstructor(const AbstractMetaClass* metaClass) const +{ + if (!metaClass) + return QString(); + + const ComplexTypeEntry* cType = reinterpret_cast(metaClass->typeEntry()); + if (cType->hasDefaultConstructor()) + return cType->defaultConstructor(); + + AbstractMetaFunctionList constructors = metaClass->queryFunctions(AbstractMetaClass::Constructors); + int maxArgs = 0; + foreach (const AbstractMetaFunction* ctor, constructors) { + if (ctor->isUserAdded() || ctor->isPrivate() || ctor->isCopyConstructor()) + continue; + int numArgs = ctor->arguments().size(); + if (numArgs == 0) { + maxArgs = 0; + break; + } + if (numArgs > maxArgs) + maxArgs = numArgs; + } + + QString qualifiedCppName = metaClass->typeEntry()->qualifiedCppName(); + QStringList templateTypes; + foreach (TypeEntry* templateType, metaClass->templateArguments()) + templateTypes << templateType->qualifiedCppName(); + QString fixedTypeName = QString("%1<%2 >").arg(qualifiedCppName).arg(templateTypes.join(", ")); + + // Empty constructor. + if (maxArgs == 0) + return QString("::%1()").arg(qualifiedCppName); + + QList candidates; + + // Constructors with C++ primitive types, enums or pointers only. + // Start with the ones with fewer arguments. + for (int i = 1; i <= maxArgs; ++i) { + foreach (const AbstractMetaFunction* ctor, constructors) { + if (ctor->isUserAdded() || ctor->isPrivate() || ctor->isCopyConstructor()) + continue; + + AbstractMetaArgumentList arguments = ctor->arguments(); + if (arguments.size() != i) + continue; + + QStringList args; + foreach (const AbstractMetaArgument* arg, arguments) { + const TypeEntry* type = arg->type()->typeEntry(); + if (type == metaClass->typeEntry()) { + args.clear(); + break; + } + + if (!arg->originalDefaultValueExpression().isEmpty()) { + if (!arg->defaultValueExpression().isEmpty() + && arg->defaultValueExpression() != arg->originalDefaultValueExpression()) { + args << arg->defaultValueExpression(); + } + break; + } + + if (type->isCppPrimitive() || type->isEnum() || isPointer(arg->type())) { + QString argValue = minimalConstructor(arg->type()); + if (argValue.isEmpty()) { + args.clear(); + break; + } + args << argValue; + } else { + args.clear(); + break; + } + } + + if (!args.isEmpty()) + return QString("::%1(%2)").arg(qualifiedCppName).arg(args.join(", ")); + + candidates << ctor; + } + } + + // Constructors with C++ primitive types, enums, pointers, value types, + // and user defined primitive types. + // Builds the minimal constructor recursively. + foreach (const AbstractMetaFunction* ctor, candidates) { + QStringList args; + foreach (const AbstractMetaArgument* arg, ctor->arguments()) { + if (arg->type()->typeEntry() == metaClass->typeEntry()) { + args.clear(); + break; + } + QString argValue = minimalConstructor(arg->type()); + if (argValue.isEmpty()) { + args.clear(); + break; + } + args << argValue; + } + if (!args.isEmpty()) { + return QString("::%1(%2)").arg(qualifiedCppName) + .arg(args.join(", ")); + } + } + + return QString(); +} + +QString Generator::translateType(const AbstractMetaType *cType, + const AbstractMetaClass *context, + Options options) const +{ + QString s; + static int constLen = strlen("const"); + + if (context && cType && + context->typeEntry()->isGenericClass() && + cType->originalTemplateType()) { + cType = cType->originalTemplateType(); + } + + if (!cType) { + s = "void"; + } else if (cType->isArray()) { + s = translateType(cType->arrayElementType(), context, options) + "[]"; + } else if (options & Generator::EnumAsInts && (cType->isEnum() || cType->isFlags())) { + s = "int"; + } else { + if (options & Generator::OriginalName) { + s = cType->originalTypeDescription().trimmed(); + if ((options & Generator::ExcludeReference) && s.endsWith("&")) + s = s.left(s.size()-1); + + // remove only the last const (avoid remove template const) + if (options & Generator::ExcludeConst) { + int index = s.lastIndexOf("const"); + + if (index >= (s.size() - (constLen + 1))) // (VarType const) or (VarType const[*|&]) + s = s.remove(index, constLen); + } + } else if (options & Generator::ExcludeConst || options & Generator::ExcludeReference) { + AbstractMetaType* copyType = cType->copy(); + + if (options & Generator::ExcludeConst) + copyType->setConstant(false); + + if (options & Generator::ExcludeReference) + copyType->setReference(false); + + s = copyType->cppSignature(); + if (!copyType->typeEntry()->isVoid() && !copyType->typeEntry()->isCppPrimitive()) + s.prepend("::"); + delete copyType; + } else { + s = cType->cppSignature(); + } + } + + return s; +} + + +QString Generator::subDirectoryForClass(const AbstractMetaClass* clazz) const +{ + return subDirectoryForPackage(clazz->package()); +} + +QString Generator::subDirectoryForPackage(QString packageName) const +{ + if (packageName.isEmpty()) + packageName = m_d->packageName; + return QString(packageName).replace(".", QDir::separator()); +} + +template +static QString getClassTargetFullName_(const T* t, bool includePackageName) +{ + QString name = t->name(); + const AbstractMetaClass* context = t->enclosingClass(); + while (context) { + name.prepend('.'); + name.prepend(context->name()); + context = context->enclosingClass(); + } + if (includePackageName) { + name.prepend('.'); + name.prepend(t->package()); + } + return name; +} + +QString getClassTargetFullName(const AbstractMetaClass* metaClass, bool includePackageName) +{ + return getClassTargetFullName_(metaClass, includePackageName); +} + +QString getClassTargetFullName(const AbstractMetaEnum* metaEnum, bool includePackageName) +{ + return getClassTargetFullName_(metaEnum, includePackageName); +} diff --git a/generator/generator.h b/generator/generator.h new file mode 100644 index 000000000..92f7219ca --- /dev/null +++ b/generator/generator.h @@ -0,0 +1,341 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef GENERATOR_H +#define GENERATOR_H + +#include +#include +#include +#include + +class ApiExtractor; +class AbstractMetaBuilder; +class QFile; + +QTextStream& formatCode(QTextStream &s, const QString& code, Indentor &indentor); +void verifyDirectoryFor(const QFile &file); + +QString getClassTargetFullName(const AbstractMetaClass* metaClass, bool includePackageName = true); +QString getClassTargetFullName(const AbstractMetaEnum* metaEnum, bool includePackageName = true); + +/** + * Base class for all generators. The default implementations does nothing, + * you must subclass this to create your own generators. + */ +class Generator +{ +public: + /// Optiosn used around the generator code + enum Option { + NoOption = 0x00000000, + BoxedPrimitive = 0x00000001, + ExcludeConst = 0x00000002, + ExcludeReference = 0x00000004, + UseNativeIds = 0x00000008, + + EnumAsInts = 0x00000010, + SkipName = 0x00000020, + NoCasts = 0x00000040, + SkipReturnType = 0x00000080, + OriginalName = 0x00000100, + ShowStatic = 0x00000200, + UnderscoreSpaces = 0x00000400, + ForceEnumCast = 0x00000800, + ArrayAsPointer = 0x00001000, + VirtualCall = 0x00002000, + SkipTemplateParameters = 0x00004000, + SkipAttributes = 0x00008000, + OriginalTypeDescription = 0x00010000, + SkipRemovedArguments = 0x00020000, + IncludeDefaultExpression = 0x00040000, + NoReturnStatement = 0x00080000, + NoBlockedSlot = 0x00100000, + + SuperCall = 0x00200000, + + GlobalRefJObject = 0x00100000, + + SkipDefaultValues = 0x00400000, + + WriteSelf = 0x00800000, + ExcludeMethodConst = 0x01000000, + + ForceValueType = ExcludeReference | ExcludeConst + }; + Q_DECLARE_FLAGS(Options, Option) + + Generator(); + virtual ~Generator(); + + bool setup(const ApiExtractor& extractor, const QMap args); + + virtual QMap options() const; + + /// Returns the classes used to generate the binding code. + AbstractMetaClassList classes() const; + + /// Returns all global functions found by APIExtractor + AbstractMetaFunctionList globalFunctions() const; + + /// Returns all global enums found by APIExtractor + AbstractMetaEnumList globalEnums() const; + + /// Returns all primitive types found by APIExtractor + QList primitiveTypes() const; + + /// Returns all container types found by APIExtractor + QList containerTypes() const; + + /// Returns an AbstractMetaEnum for a given EnumTypeEntry, or NULL if not found. + const AbstractMetaEnum* findAbstractMetaEnum(const EnumTypeEntry* typeEntry) const; + + /// Returns an AbstractMetaEnum for a given TypeEntry that is an EnumTypeEntry, or NULL if not found. + const AbstractMetaEnum* findAbstractMetaEnum(const TypeEntry* typeEntry) const; + + /// Returns an AbstractMetaEnum for the enum related to a given FlagsTypeEntry, or NULL if not found. + const AbstractMetaEnum* findAbstractMetaEnum(const FlagsTypeEntry* typeEntry) const; + + /// Returns an AbstractMetaEnum for a given AbstractMetaType that holds an EnumTypeEntry, or NULL if not found. + const AbstractMetaEnum* findAbstractMetaEnum(const AbstractMetaType* metaType) const; + + /// Returns the output directory + QString outputDirectory() const; + + /// Set the output directory + void setOutputDirectory(const QString &outDir); + + /** + * Start the code generation, be sure to call setClasses before callign this method. + * For each class it creates a QTextStream, call the write method with the current + * class and the associated text stream, then write the text stream contents if needed. + * \see #write + */ + void generate(); + + /// Returns the number of generated items + int numGenerated() const; + + /// Returns the number of generated items written + int numGeneratedAndWritten() const; + + /// Returns the generator's name. Used for cosmetic purposes. + virtual const char* name() const = 0; + + /// Returns true if the generator should generate any code for the TypeEntry. + bool shouldGenerateTypeEntry(const TypeEntry*) const; + + /// Returns true if the generator should generate any code for the AbstractMetaClass. + virtual bool shouldGenerate(const AbstractMetaClass *) const; + + /// Returns the subdirectory used to write the binding code of an AbstractMetaClass. + virtual QString subDirectoryForClass(const AbstractMetaClass* clazz) const; + + /** + * Translate metatypes to binding source format. + * \param metatype a pointer to metatype + * \param context the current meta class + * \param option some extra options + * \return the metatype translated to binding source format + */ + QString translateType(const AbstractMetaType *metatype, + const AbstractMetaClass *context, + Options options = NoOption) const; + + /** + * Function used to write the fucntion arguments on the class buffer. + * \param s the class output buffer + * \param metafunction the pointer to metafunction information + * \param count the number of function arguments + * \param options some extra options used during the parser + */ + virtual void writeFunctionArguments(QTextStream &s, + const AbstractMetaFunction *metafunction, + Options options = NoOption) const = 0; + + virtual void writeArgumentNames(QTextStream &s, + const AbstractMetaFunction *metafunction, + Options options = NoOption) const = 0; + + void replaceTemplateVariables(QString &code, const AbstractMetaFunction *func); + + // QtScript + QSet qtMetaTypeDeclaredTypeNames() const; + + /** + * Returns the license comment to be prepended to each source file generated. + */ + QString licenseComment() const; + + /** + * Sets the license comment to be prepended to each source file generated. + */ + void setLicenseComment(const QString &licenseComment); + + /** + * Returns the package name. + */ + QString packageName() const; + + /** + * Retrieves the name of the currently processed module. + * While package name is a complete package idetification, e.g. 'PySide.QtCore', + * a module name represents the last part of the package, e.g. 'QtCore'. + * If the target language separates the modules with characters other than + * dots ('.') the generator subclass must overload this method. + * \return a string representing the last part of a package name + */ + virtual QString moduleName() const; + + /** + * Retrieves a list of constructors used in implicit conversions + * available on the given type. The TypeEntry must be a value-type + * or else it will return an empty list. + * \param type a TypeEntry that is expected to be a value-type + * \return a list of constructors that could be used as implicit converters + */ + AbstractMetaFunctionList implicitConversions(const TypeEntry* type) const; + + /// Convenience function for implicitConversions(const TypeEntry* type). + AbstractMetaFunctionList implicitConversions(const AbstractMetaType* metaType) const; + + /// Check if type is a pointer. + static bool isPointer(const AbstractMetaType* type); + + /// Tells if the type or class is an Object (or QObject) Type. + static bool isObjectType(const TypeEntry* type); + static bool isObjectType(const ComplexTypeEntry* type); + static bool isObjectType(const AbstractMetaType* metaType); + static bool isObjectType(const AbstractMetaClass* metaClass); + + /// Returns true if the type is a C string (const char*). + static bool isCString(const AbstractMetaType* type); + /// Returns true if the type is a void pointer. + static bool isVoidPointer(const AbstractMetaType* type); + + // Returns the full name of the type. + QString getFullTypeName(const TypeEntry* type) const; + QString getFullTypeName(const AbstractMetaType* type) const; + QString getFullTypeName(const AbstractMetaClass* metaClass) const; + + /** + * Returns the full qualified C++ name for an AbstractMetaType, but removing modifiers + * as 'const', '&', and '*' (except if the class is not derived from a template). + * This is useful for instantiated templates. + */ + QString getFullTypeNameWithoutModifiers(const AbstractMetaType* type) const; + + /** + * Tries to build a minimal constructor for the type. + * It will check first for a user defined default constructor. + * Returns a null string if it fails. + */ + QString minimalConstructor(const TypeEntry* type) const; + QString minimalConstructor(const AbstractMetaType* type) const; + QString minimalConstructor(const AbstractMetaClass* metaClass) const; +protected: + /** + * Returns the file name used to write the binding code of an AbstractMetaClass. + * \param metaClass the AbstractMetaClass for which the file name must be + * returned + * \return the file name used to write the binding code for the class + */ + virtual QString fileNameForClass(const AbstractMetaClass* metaClass) const = 0; + + + virtual bool doSetup(const QMap& args) = 0; + + /** + * Write the bindding code for an AbstractMetaClass. + * This is called by generate method. + * \param s text stream to write the generated output + * \param metaClass the class that should be generated + */ + virtual void generateClass(QTextStream& s, const AbstractMetaClass* metaClass) = 0; + virtual void finishGeneration() = 0; + + /** + * Returns the subdirectory path for a given package + * (aka module, aka library) name. + * If the target language separates the package modules with characters other + * than dots ('.') the generator subclass must overload this method. + * /param packageName complete package name for which to return the subdirectory path + * or nothing the use the name of the currently processed package + * /return a string representing the subdirectory path for the given package + */ + virtual QString subDirectoryForPackage(QString packageName = QString()) const; + + QList instantiatedContainers() const; + + static QString getSimplifiedContainerTypeName(const AbstractMetaType* type); + void addInstantiatedContainers(const AbstractMetaType* type); + +private: + struct GeneratorPrivate; + GeneratorPrivate* m_d; + void collectInstantiatedContainers(const AbstractMetaFunction* func); + void collectInstantiatedContainers(const AbstractMetaClass* metaClass); + void collectInstantiatedContainers(); +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(Generator::Options) +typedef QLinkedList GeneratorList; + +/** +* Utility class to store the identation level, use it in a QTextStream. +*/ +class Indentor +{ +public: + Indentor() : indent(0) {} + int indent; +}; + +/** +* Class that use the RAII idiom to set and unset the identation level. +*/ +class Indentation +{ +public: + Indentation(Indentor &indentor) : indentor(indentor) + { + indentor.indent++; + } + ~Indentation() + { + indentor.indent--; + } + +private: + Indentor &indentor; +}; + +inline QTextStream &operator <<(QTextStream &s, const Indentor &indentor) +{ + for (int i = 0; i < indentor.indent; ++i) + s << " "; + return s; +} + +#endif // GENERATOR_H + diff --git a/generator/main.cpp b/generator/main.cpp new file mode 100644 index 000000000..f9f94f7ac --- /dev/null +++ b/generator/main.cpp @@ -0,0 +1,443 @@ +/* + * This file is part of the PySide project. + * + * Copyright (C) 2009-2012 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include "generator.h" +#include "shibokenconfig.h" +#include "cppgenerator.h" +#include "headergenerator.h" +#include "qtdocgenerator.h" + +#ifdef _WINDOWS + #define PATH_SPLITTER ";" +#else + #define PATH_SPLITTER ":" +#endif + +namespace { + +class ArgsHandler +{ +public: + explicit ArgsHandler(const QMap& other); + virtual ~ArgsHandler(); + + inline QMap& args() const + { + return *m_args; + } + + inline bool argExists(const QString& s) const + { + return m_args->contains(s); + } + + QString removeArg(const QString& s); + bool argExistsRemove(const QString& s); + + inline QString argValue(const QString& s) const + { + return m_args->value(s); + } + + inline bool noArgs() const + { + return m_args->isEmpty(); + } + +private: + QMap* m_args; +}; + +ArgsHandler::ArgsHandler(const QMap& other) + : m_args(new QMap(other)) +{ +} + +ArgsHandler::~ArgsHandler() +{ + delete m_args; +} + +QString ArgsHandler::removeArg(const QString& s) +{ + QString retval; + + if (argExists(s)) { + retval = argValue(s); + m_args->remove(s); + } + + return retval; +} + +bool ArgsHandler::argExistsRemove(const QString& s) +{ + bool retval = false; + + if (argExists(s)) { + retval = true; + m_args->remove(s); + } + + return retval; +} + +} + +static void printOptions(QTextStream& s, const QMap& options) +{ + QMap::const_iterator it = options.constBegin(); + s.setFieldAlignment(QTextStream::AlignLeft); + for (; it != options.constEnd(); ++it) { + s << " --"; + s.setFieldWidth(38); + s << it.key() << it.value(); + s.setFieldWidth(0); + s << endl; + } +} + +typedef void (*getGeneratorsFunc)(QLinkedList*); + +static bool processProjectFile(QFile& projectFile, QMap& args) +{ + QByteArray line = projectFile.readLine().trimmed(); + if (line.isEmpty() || line != "[generator-project]") + return false; + + QStringList includePaths; + QStringList typesystemPaths; + QStringList apiVersions; + + while (!projectFile.atEnd()) { + line = projectFile.readLine().trimmed(); + if (line.isEmpty()) + continue; + + int split = line.indexOf("="); + QString key; + QString value; + if (split > 0) { + key = line.left(split - 1).trimmed(); + value = line.mid(split + 1).trimmed(); + } else { + key = line; + } + + if (key == "include-path") + includePaths << QDir::toNativeSeparators(value); + else if (key == "typesystem-path") + typesystemPaths << QDir::toNativeSeparators(value); + else if (key == "api-version") + apiVersions << value; + else if (key == "header-file") + args["arg-1"] = value; + else if (key == "typesystem-file") + args["arg-2"] = value; + else + args[key] = value; + } + + if (!includePaths.isEmpty()) + args["include-paths"] = includePaths.join(PATH_SPLITTER); + + if (!typesystemPaths.isEmpty()) + args["typesystem-paths"] = typesystemPaths.join(PATH_SPLITTER); + if (!apiVersions.isEmpty()) + args["api-version"] = apiVersions.join("|"); + return true; +} + +static QMap getInitializedArguments() +{ + QMap args; + QStringList arguments = QCoreApplication::arguments(); + QString appName = arguments.first(); + arguments.removeFirst(); + + QString projectFileName; + foreach (const QString& arg, arguments) { + if (arg.startsWith("--project-file")) { + int split = arg.indexOf("="); + if (split > 0) + projectFileName = arg.mid(split + 1).trimmed(); + break; + } + } + + if (projectFileName.isNull()) + return args; + + if (!QFile::exists(projectFileName)) { + std::cerr << qPrintable(appName) << ": Project file \""; + std::cerr << qPrintable(projectFileName) << "\" not found."; + std::cerr << std::endl; + return args; + } + + QFile projectFile(projectFileName); + if (!projectFile.open(QIODevice::ReadOnly)) + return args; + + if (!processProjectFile(projectFile, args)) { + std::cerr << qPrintable(appName) << ": first line of project file \""; + std::cerr << qPrintable(projectFileName) << "\" must be the string \"[generator-project]\""; + std::cerr << std::endl; + return args; + } + + return args; +} + +static QMap getCommandLineArgs() +{ + QMap args = getInitializedArguments(); + QStringList arguments = QCoreApplication::arguments(); + arguments.removeFirst(); + + int argNum = 0; + foreach (QString arg, arguments) { + arg = arg.trimmed(); + if (arg.startsWith("--")) { + int split = arg.indexOf("="); + if (split > 0) + args[arg.mid(2).left(split-2)] = arg.mid(split + 1).trimmed(); + else + args[arg.mid(2)] = QString(); + } else if (arg.startsWith("-")) { + args[arg.mid(1)] = QString(); + } else { + argNum++; + args[QString("arg-%1").arg(argNum)] = arg; + } + } + return args; +} + +void printUsage(const GeneratorList& generators) +{ + QTextStream s(stdout); + s << "Usage:\n " + << "shiboken [options] header-file typesystem-file\n\n" + << "General options:\n"; + QMap generalOptions; + generalOptions.insert("project-file=", "text file containing a description of the binding project. Replaces and overrides command line arguments"); + generalOptions.insert("debug-level=[sparse|medium|full]", "Set the debug level"); + generalOptions.insert("silent", "Avoid printing any message"); + generalOptions.insert("help", "Display this help and exit"); + generalOptions.insert("no-suppress-warnings", "Show all warnings"); + generalOptions.insert("output-directory=", "The directory where the generated files will be written"); + generalOptions.insert("include-paths=[" PATH_SPLITTER "" PATH_SPLITTER "...]", "Include paths used by the C++ parser"); + generalOptions.insert("typesystem-paths=[" PATH_SPLITTER "" PATH_SPLITTER "...]", "Paths used when searching for typesystems"); + generalOptions.insert("documentation-only", "Do not generates any code, just the documentation"); + generalOptions.insert("license-file=", "File used for copyright headers of generated files"); + generalOptions.insert("version", "Output version information and exit"); + generalOptions.insert("generator-set=<\"generator module\">", "generator-set to be used. e.g. qtdoc"); + generalOptions.insert("api-version=<\"package mask\">,<\"version\">", "Specify the supported api version used to generate the bindings"); + generalOptions.insert("drop-type-entries=\"[;TypeEntry1;...]\"", "Semicolon separated list of type system entries (classes, namespaces, global functions and enums) to be dropped from generation."); + printOptions(s, generalOptions); + + foreach (Generator* generator, generators) { + QMap options = generator->options(); + if (!options.isEmpty()) { + s << endl << generator->name() << " options:\n"; + printOptions(s, generator->options()); + } + } +} + +static inline void printVerAndBanner() +{ + std::cout << "shiboken v" SHIBOKEN_VERSION << std::endl; + std::cout << "Copyright (C) 2009-2012 Nokia Corporation and/or its subsidiary(-ies)" << std::endl; +} + +static inline void errorPrint(const QString& s, + const bool& verAndBanner = false) +{ + if (verAndBanner) + printVerAndBanner(); + + std::cerr << s.toAscii().constData() << std::endl; +} + +int main(int argc, char *argv[]) +{ + // needed by qxmlpatterns + QCoreApplication app(argc, argv); + + // Store command arguments in a map + QMap args = getCommandLineArgs(); + ArgsHandler argsHandler(args); + GeneratorList generators; + + if (argsHandler.argExistsRemove("version")) { + printVerAndBanner(); + return EXIT_SUCCESS; + } + + QString generatorSet = argsHandler.removeArg("generator-set"); + // Also check "generatorSet" command line argument for backward compatibility. + if (generatorSet.isEmpty()) + generatorSet = argsHandler.removeArg("generatorSet"); + + // Pre-defined generator sets. + if (generatorSet == "qtdoc") { + generators << new QtDocGenerator; + } else if (generatorSet.isEmpty() || generatorSet == "shiboken") { + generators << new CppGenerator << new HeaderGenerator; + } else { + errorPrint("shiboken: Unknown generator set, try \"shiboken\" or \"qtdoc\"."); + return EXIT_FAILURE; + } + + if (argsHandler.argExistsRemove("help")) { + printUsage(generators); + return EXIT_SUCCESS; + } + + QString licenseComment; + QString licenseFileName = argsHandler.removeArg("license-file"); + if (!licenseFileName.isEmpty()) { + if (QFile::exists(licenseFileName)) { + QFile licenseFile(licenseFileName); + if (licenseFile.open(QIODevice::ReadOnly)) + licenseComment = licenseFile.readAll(); + } else { + errorPrint(QString("Couldn't find the file containing the license heading: %1"). + arg(qPrintable(licenseFileName))); + return EXIT_FAILURE; + } + } + + QString outputDirectory = argsHandler.removeArg("output-directory"); + if (outputDirectory.isEmpty()) + outputDirectory = "out"; + + if (!QDir(outputDirectory).exists()) { + if (!QDir().mkpath(outputDirectory)) { + ReportHandler::warning("Can't create output directory: "+outputDirectory); + return EXIT_FAILURE; + } + } + + // Create and set-up API Extractor + ApiExtractor extractor; + extractor.setLogDirectory(outputDirectory); + + if (argsHandler.argExistsRemove("silent")) { + extractor.setSilent(true); + } else { + QString level = argsHandler.removeArg("debug-level"); + if (!level.isEmpty()) { + if (level == "sparse") + extractor.setDebugLevel(ReportHandler::SparseDebug); + else if (level == "medium") + extractor.setDebugLevel(ReportHandler::MediumDebug); + else if (level == "full") + extractor.setDebugLevel(ReportHandler::FullDebug); + } + } + if (argsHandler.argExistsRemove("no-suppress-warnings")) + extractor.setSuppressWarnings(false); + + if (argsHandler.argExists("api-version")) { + QStringList versions = argsHandler.removeArg("api-version").split("|"); + foreach (QString fullVersion, versions) { + QStringList parts = fullVersion.split(","); + QString package; + QString version; + package = parts.count() == 1 ? "*" : parts.first(); + version = parts.last(); + extractor.setApiVersion(package, version.toAscii()); + } + } + + if (argsHandler.argExists("drop-type-entries")) + extractor.setDropTypeEntries(argsHandler.removeArg("drop-type-entries")); + + QString path = argsHandler.removeArg("typesystem-paths"); + if (!path.isEmpty()) + extractor.addTypesystemSearchPath(path.split(PATH_SPLITTER)); + + path = argsHandler.removeArg("include-paths"); + if (!path.isEmpty()) + extractor.addIncludePath(path.split(PATH_SPLITTER)); + + QString cppFileName = argsHandler.removeArg("arg-1"); + QString typeSystemFileName = argsHandler.removeArg("arg-2"); + + /* Make sure to remove the project file's arguments (if any) and + * --project-file, also the arguments of each generator before + * checking if there isn't any existing arguments in argsHandler. + */ + argsHandler.removeArg("project-file"); + QMap projectFileArgs = getInitializedArguments(); + if (!projectFileArgs.isEmpty()) { + QMap::const_iterator it = + projectFileArgs.constBegin(); + for ( ; it != projectFileArgs.constEnd(); ++it) + argsHandler.removeArg(it.key()); + } + foreach (Generator* generator, generators) { + QMap options = generator->options(); + if (!options.isEmpty()) { + QMap::const_iterator it = options.constBegin(); + for ( ; it != options.constEnd(); ++it) + argsHandler.removeArg(it.key()); + } + } + + if (!argsHandler.noArgs()) { + errorPrint("shiboken: Called with wrong arguments."); + std::cout << "Note: use --help option for more information." << std::endl; + return EXIT_FAILURE; + } + + extractor.setCppFileName(cppFileName); + extractor.setTypeSystem(typeSystemFileName); + if (!extractor.run()) + return EXIT_FAILURE; + + if (!extractor.classCount()) + ReportHandler::warning("No C++ classes found!"); + + foreach (Generator* g, generators) { + g->setOutputDirectory(outputDirectory); + g->setLicenseComment(licenseComment); + if (g->setup(extractor, args)) + g->generate(); + } + qDeleteAll(generators); + + ReportHandler::flush(); + std::cout << "Done, " << ReportHandler::warningCount(); + std::cout << " warnings (" << ReportHandler::suppressedCount() << " known issues)"; + std::cout << std::endl; +} diff --git a/generator/qtdoc/CMakeLists.txt b/generator/qtdoc/CMakeLists.txt new file mode 100644 index 000000000..541e7c6ee --- /dev/null +++ b/generator/qtdoc/CMakeLists.txt @@ -0,0 +1,21 @@ +project(qtdoc_generator) + +set(qtdoc_generator_SRC +qtdocgenerator.cpp +) + +include_directories(${generators_SOURCE_DIR} + ${QT_QTCORE_INCLUDE_DIR} + ${APIEXTRACTOR_INCLUDE_DIR}) +add_executable(docgenerator main.cpp) +set_target_properties(docgenerator PROPERTIES OUTPUT_NAME docgenerator${generator_SUFFIX}) + +target_link_libraries(docgenerator ${QT_QTCORE_LIBRARY}) + +add_library(qtdoc_generator SHARED ${qtdoc_generator_SRC}) +target_link_libraries(qtdoc_generator ${APIEXTRACTOR_LIBRARY} ${QT_QTCORE_LIBRARY} genrunner) +set_property(TARGET qtdoc_generator PROPERTY PREFIX "") + +install(TARGETS qtdoc_generator DESTINATION ${generator_plugin_DIR}) +install(TARGETS docgenerator DESTINATION bin) + diff --git a/generator/qtdoc/qtdocgenerator.cpp b/generator/qtdoc/qtdocgenerator.cpp new file mode 100644 index 000000000..593456405 --- /dev/null +++ b/generator/qtdoc/qtdocgenerator.cpp @@ -0,0 +1,1652 @@ +/* + * This file is part of the PySide project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "qtdocgenerator.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static Indentor INDENT; + +static bool shouldSkip(const AbstractMetaFunction* func) +{ + bool skipable = func->isConstructor() + || func->isModifiedRemoved() + || func->declaringClass() != func->ownerClass() + || func->isCastOperator() + || func->name() == "operator="; + + // Search a const clone + if (!skipable && !func->isConstant()) { + const AbstractMetaArgumentList funcArgs = func->arguments(); + foreach (AbstractMetaFunction* f, func->ownerClass()->functions()) { + if (f != func + && f->isConstant() + && f->name() == func->name() + && f->arguments().count() == funcArgs.count()) { + // Compare each argument + bool cloneFound = true; + + const AbstractMetaArgumentList fargs = f->arguments(); + for (int i = 0, max = funcArgs.count(); i < max; ++i) { + if (funcArgs.at(i)->type()->typeEntry() != fargs.at(i)->type()->typeEntry()) { + cloneFound = false; + break; + } + } + if (cloneFound) + return true; + } + } + } + return skipable; +} + +static bool functionSort(const AbstractMetaFunction* func1, const AbstractMetaFunction* func2) +{ + return func1->name() < func2->name(); +} + +static QString createRepeatedChar(int i, char c) +{ + QString out; + for (int j = 0; j < i; ++j) + out += c; + + return out; +} + +static QString escape(QString& str) +{ + return str + .replace("*", "\\*") + .replace("_", "\\_"); +} + +static QString escape(const QStringRef& strref) +{ + QString str = strref.toString(); + return escape(str); +} + + +QtXmlToSphinx::QtXmlToSphinx(QtDocGenerator* generator, const QString& doc, const QString& context) + : m_context(context), m_generator(generator), m_insideBold(false), m_insideItalic(false) +{ + m_handlerMap.insert("heading", &QtXmlToSphinx::handleHeadingTag); + m_handlerMap.insert("brief", &QtXmlToSphinx::handleParaTag); + m_handlerMap.insert("para", &QtXmlToSphinx::handleParaTag); + m_handlerMap.insert("italic", &QtXmlToSphinx::handleItalicTag); + m_handlerMap.insert("bold", &QtXmlToSphinx::handleBoldTag); + m_handlerMap.insert("see-also", &QtXmlToSphinx::handleSeeAlsoTag); + m_handlerMap.insert("snippet", &QtXmlToSphinx::handleSnippetTag); + m_handlerMap.insert("dots", &QtXmlToSphinx::handleDotsTag); + m_handlerMap.insert("codeline", &QtXmlToSphinx::handleDotsTag); + m_handlerMap.insert("table", &QtXmlToSphinx::handleTableTag); + m_handlerMap.insert("header", &QtXmlToSphinx::handleRowTag); + m_handlerMap.insert("row", &QtXmlToSphinx::handleRowTag); + m_handlerMap.insert("item", &QtXmlToSphinx::handleItemTag); + m_handlerMap.insert("argument", &QtXmlToSphinx::handleArgumentTag); + m_handlerMap.insert("teletype", &QtXmlToSphinx::handleArgumentTag); + m_handlerMap.insert("link", &QtXmlToSphinx::handleLinkTag); + m_handlerMap.insert("inlineimage", &QtXmlToSphinx::handleImageTag); + m_handlerMap.insert("image", &QtXmlToSphinx::handleImageTag); + m_handlerMap.insert("list", &QtXmlToSphinx::handleListTag); + m_handlerMap.insert("term", &QtXmlToSphinx::handleTermTag); + m_handlerMap.insert("raw", &QtXmlToSphinx::handleRawTag); + m_handlerMap.insert("underline", &QtXmlToSphinx::handleItalicTag); + m_handlerMap.insert("superscript", &QtXmlToSphinx::handleSuperScriptTag); + m_handlerMap.insert("code", &QtXmlToSphinx::handleCodeTag); + m_handlerMap.insert("badcode", &QtXmlToSphinx::handleCodeTag); + m_handlerMap.insert("legalese", &QtXmlToSphinx::handleCodeTag); + m_handlerMap.insert("section", &QtXmlToSphinx::handleAnchorTag); + m_handlerMap.insert("quotefile", &QtXmlToSphinx::handleQuoteFileTag); + + // ignored tags + m_handlerMap.insert("generatedlist", &QtXmlToSphinx::handleIgnoredTag); + m_handlerMap.insert("tableofcontents", &QtXmlToSphinx::handleIgnoredTag); + m_handlerMap.insert("quotefromfile", &QtXmlToSphinx::handleIgnoredTag); + m_handlerMap.insert("skipto", &QtXmlToSphinx::handleIgnoredTag); + m_handlerMap.insert("target", &QtXmlToSphinx::handleIgnoredTag); + + // useless tags + m_handlerMap.insert("description", &QtXmlToSphinx::handleUselessTag); + m_handlerMap.insert("definition", &QtXmlToSphinx::handleUselessTag); + m_handlerMap.insert("printuntil", &QtXmlToSphinx::handleUselessTag); + m_handlerMap.insert("relation", &QtXmlToSphinx::handleUselessTag); + + // Doxygen tags + m_handlerMap.insert("title", &QtXmlToSphinx::handleHeadingTag); + m_handlerMap.insert("ref", &QtXmlToSphinx::handleParaTag); + m_handlerMap.insert("computeroutput", &QtXmlToSphinx::handleParaTag); + m_handlerMap.insert("detaileddescription", &QtXmlToSphinx::handleParaTag); + m_handlerMap.insert("name", &QtXmlToSphinx::handleParaTag); + m_handlerMap.insert("listitem", &QtXmlToSphinx::handleItemTag); + m_handlerMap.insert("parametername", &QtXmlToSphinx::handleItemTag); + m_handlerMap.insert("parameteritem", &QtXmlToSphinx::handleItemTag); + m_handlerMap.insert("ulink", &QtXmlToSphinx::handleLinkTag); + m_handlerMap.insert("itemizedlist", &QtXmlToSphinx::handleListTag); + m_handlerMap.insert("parameternamelist", &QtXmlToSphinx::handleListTag); + m_handlerMap.insert("parameterlist", &QtXmlToSphinx::handleListTag); + + // Doxygen ignored tags + m_handlerMap.insert("highlight", &QtXmlToSphinx::handleIgnoredTag); + m_handlerMap.insert("linebreak", &QtXmlToSphinx::handleIgnoredTag); + m_handlerMap.insert("programlisting", &QtXmlToSphinx::handleIgnoredTag); + m_handlerMap.insert("xreftitle", &QtXmlToSphinx::handleIgnoredTag); + m_handlerMap.insert("sp", &QtXmlToSphinx::handleIgnoredTag); + m_handlerMap.insert("entry", &QtXmlToSphinx::handleIgnoredTag); + m_handlerMap.insert("simplesect", &QtXmlToSphinx::handleIgnoredTag); + m_handlerMap.insert("verbatim", &QtXmlToSphinx::handleIgnoredTag); + m_handlerMap.insert("xrefsect", &QtXmlToSphinx::handleIgnoredTag); + m_handlerMap.insert("xrefdescription", &QtXmlToSphinx::handleIgnoredTag); + + m_result = transform(doc); +} + +void QtXmlToSphinx::pushOutputBuffer() +{ + QString* buffer = new QString(); + m_buffers << buffer; + m_output.setString(buffer); +} + +QString QtXmlToSphinx::popOutputBuffer() +{ + Q_ASSERT(!m_buffers.isEmpty()); + QString* str = m_buffers.pop(); + QString strcpy(*str); + delete str; + m_output.setString(m_buffers.isEmpty() ? 0 : m_buffers.top()); + return strcpy; +} + +QString QtXmlToSphinx::expandFunction(const QString& function) +{ + QStringList functionSpec = function.split('.'); + QString className = functionSpec.first(); + const AbstractMetaClass* metaClass = 0; + foreach (const AbstractMetaClass* cls, m_generator->classes()) { + if (cls->name() == className) { + metaClass = cls; + break; + } + } + + if (metaClass) { + functionSpec.removeFirst(); + return metaClass->typeEntry()->qualifiedTargetLangName() + "." + functionSpec.join("."); + } else { + return function; + } +} + +QString QtXmlToSphinx::resolveContextForMethod(const QString& methodName) +{ + QString currentClass = m_context.split(".").last(); + + const AbstractMetaClass* metaClass = 0; + foreach (const AbstractMetaClass* cls, m_generator->classes()) { + if (cls->name() == currentClass) { + metaClass = cls; + break; + } + } + + if (metaClass) { + QList funcList; + foreach (const AbstractMetaFunction* func, metaClass->queryFunctionsByName(methodName)) { + if (methodName == func->name()) + funcList.append(func); + } + + const AbstractMetaClass* implementingClass = 0; + foreach (const AbstractMetaFunction* func, funcList) { + implementingClass = func->implementingClass(); + if (implementingClass->name() == currentClass) + break; + } + + if (implementingClass) + return implementingClass->typeEntry()->qualifiedTargetLangName(); + } + + return QLatin1String("~") + m_context; +} + +QString QtXmlToSphinx::transform(const QString& doc) +{ + Q_ASSERT(m_buffers.isEmpty()); + Indentation indentation(INDENT); + if (doc.trimmed().isEmpty()) + return doc; + + pushOutputBuffer(); + + QXmlStreamReader reader(doc); + + while (!reader.atEnd()) { + QXmlStreamReader::TokenType token = reader.readNext(); + if (reader.hasError()) { + m_output << INDENT << "XML Error: " + reader.errorString() + "\n" + doc; + ReportHandler::warning("XML Error: " + reader.errorString() + "\n" + doc); + break; + } + + if (token == QXmlStreamReader::StartElement) { + QStringRef tagName = reader.name(); + TagHandler handler = m_handlerMap.value(tagName.toString(), &QtXmlToSphinx::handleUnknownTag); + if (!m_handlers.isEmpty() && ( (m_handlers.top() == &QtXmlToSphinx::handleIgnoredTag) || + (m_handlers.top() == &QtXmlToSphinx::handleRawTag)) ) + handler = &QtXmlToSphinx::handleIgnoredTag; + + m_handlers.push(handler); + } + if (!m_handlers.isEmpty()) + (this->*(m_handlers.top()))(reader); + + if (token == QXmlStreamReader::EndElement) { + m_handlers.pop(); + m_lastTagName = reader.name().toString(); + } + } + m_output.flush(); + QString retval = popOutputBuffer(); + Q_ASSERT(m_buffers.isEmpty()); + return retval; +} + +QString QtXmlToSphinx::readFromLocations(const QStringList& locations, const QString& path, const QString& identifier) +{ + QString result; + bool ok; + foreach (QString location, locations) { + location.append('/'); + location.append(path); + result = readFromLocation(location, identifier, &ok); + if (ok) + break; + } + if (!ok) + ReportHandler::warning("Couldn't read code snippet file: {"+ locations.join("|") + '}' + path); + return result; +} + +QString QtXmlToSphinx::readFromLocation(const QString& location, const QString& identifier, bool* ok) +{ + QFile inputFile; + inputFile.setFileName(location); + if (!inputFile.open(QIODevice::ReadOnly)) { + if (!ok) + ReportHandler::warning("Couldn't read code snippet file: "+inputFile.fileName()); + else + *ok = false; + return QString(); + } + + QRegExp searchString("//!\\s*\\[" + identifier + "\\]"); + QRegExp codeSnippetCode("//!\\s*\\[[\\w\\d\\s]+\\]"); + QString code; + QString line; + bool identifierIsEmpty = identifier.isEmpty(); + bool getCode = false; + + while (!inputFile.atEnd()) { + line = inputFile.readLine(); + if (identifierIsEmpty) { + code += line; + } else if (getCode && !line.contains(searchString)) { + code += line.replace(codeSnippetCode, ""); + } else if (line.contains(searchString)) { + if (getCode) + break; + else + getCode = true; + } + } + + if (!identifierIsEmpty && !getCode) + ReportHandler::warning("Code snippet file found ("+location+"), but snippet "+ identifier +" not found."); + + if (ok) + *ok = true; + return code; +} + +void QtXmlToSphinx::handleHeadingTag(QXmlStreamReader& reader) +{ + static QString heading; + static char type; + static char types[] = { '-', '^' }; + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + uint typeIdx = reader.attributes().value("level").toString().toInt(); + if (typeIdx >= sizeof(types)) + type = types[sizeof(types)-1]; + else + type = types[typeIdx]; + } else if (token == QXmlStreamReader::EndElement) { + m_output << createRepeatedChar(heading.length(), type) << endl << endl; + } else if (token == QXmlStreamReader::Characters) { + heading = escape(reader.text()).trimmed(); + m_output << endl << endl << heading << endl; + } +} + +void QtXmlToSphinx::handleParaTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + pushOutputBuffer(); + } else if (token == QXmlStreamReader::EndElement) { + QString result = popOutputBuffer().simplified(); + if (result.startsWith("**Warning:**")) + result.replace(0, 12, ".. warning:: "); + else if (result.startsWith("**Note:**")) + result.replace(0, 9, ".. note:: "); + + m_output << INDENT << result << endl << endl; + } else if (token == QXmlStreamReader::Characters) { + QString text = escape(reader.text()); + if (!m_output.string()->isEmpty()) { + QChar start = text[0]; + QChar end = m_output.string()->at(m_output.string()->length() - 1); + if ((end == '*' || end == '`') && start != ' ' && !start.isPunct()) + m_output << '\\'; + } + m_output << INDENT << text; + } +} + +void QtXmlToSphinx::handleItalicTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement || token == QXmlStreamReader::EndElement) { + m_insideItalic = !m_insideItalic; + m_output << '*'; + } else if (token == QXmlStreamReader::Characters) { + m_output << escape(reader.text()).trimmed(); + } +} + +void QtXmlToSphinx::handleBoldTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement || token == QXmlStreamReader::EndElement) { + m_insideBold = !m_insideBold; + m_output << "**"; + } else if (token == QXmlStreamReader::Characters) { + m_output << escape(reader.text()).trimmed(); + } +} + +void QtXmlToSphinx::handleArgumentTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement || token == QXmlStreamReader::EndElement) + m_output << "``"; + else if (token == QXmlStreamReader::Characters) + m_output << reader.text().toString().trimmed(); +} + +void QtXmlToSphinx::handleSeeAlsoTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) + m_output << INDENT << ".. seealso:: "; + else if (token == QXmlStreamReader::EndElement) + m_output << endl; +} + +void QtXmlToSphinx::handleSnippetTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + bool consecutiveSnippet = m_lastTagName == "snippet" || m_lastTagName == "dots" || m_lastTagName == "codeline"; + if (consecutiveSnippet) { + m_output.flush(); + m_output.string()->chop(2); + } + QString location = reader.attributes().value("location").toString(); + QString identifier = reader.attributes().value("identifier").toString(); + QString code = readFromLocations(m_generator->codeSnippetDirs(), location, identifier); + if (!consecutiveSnippet) + m_output << INDENT << "::\n\n"; + + Indentation indentation(INDENT); + if (code.isEmpty()) { + m_output << INDENT << "" << endl; + } else { + foreach (QString line, code.split("\n")) { + if (!QString(line).trimmed().isEmpty()) + m_output << INDENT << line; + + m_output << endl; + } + } + m_output << endl; + } +} +void QtXmlToSphinx::handleDotsTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + bool consecutiveSnippet = m_lastTagName == "snippet" || m_lastTagName == "dots" || m_lastTagName == "codeline"; + if (consecutiveSnippet) { + m_output.flush(); + m_output.string()->chop(2); + } + Indentation indentation(INDENT); + pushOutputBuffer(); + m_output << INDENT; + int indent = reader.attributes().value("indent").toString().toInt(); + for (int i = 0; i < indent; ++i) + m_output << ' '; + } else if (token == QXmlStreamReader::Characters) { + m_output << reader.text().toString(); + } else if (token == QXmlStreamReader::EndElement) { + m_output << popOutputBuffer() << "\n\n\n"; + } +} + +void QtXmlToSphinx::handleTableTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + m_currentTable.clear(); + m_tableHasHeader = false; + } else if (token == QXmlStreamReader::EndElement) { + // write the table on m_output + m_currentTable.enableHeader(m_tableHasHeader); + m_currentTable.normalize(); + m_output << m_currentTable; + m_currentTable.clear(); + } +} + +void QtXmlToSphinx::handleTermTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + pushOutputBuffer(); + } else if (token == QXmlStreamReader::Characters) { + m_output << reader.text().toString().replace("::", "."); + } else if (token == QXmlStreamReader::EndElement) { + TableCell cell; + cell.data = popOutputBuffer().trimmed(); + m_currentTable << (TableRow() << cell); + } +} + + +void QtXmlToSphinx::handleItemTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + if (m_currentTable.isEmpty()) + m_currentTable << TableRow(); + TableRow& row = m_currentTable.last(); + TableCell cell; + cell.colSpan = reader.attributes().value("colspan").toString().toShort(); + cell.rowSpan = reader.attributes().value("rowspan").toString().toShort(); + row << cell; + pushOutputBuffer(); + } else if (token == QXmlStreamReader::EndElement) { + QString data = popOutputBuffer().trimmed(); + if (!m_currentTable.isEmpty()) { + TableRow& row = m_currentTable.last(); + if (!row.isEmpty()) + row.last().data = data; + } + } +} + +void QtXmlToSphinx::handleRowTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + m_tableHasHeader = reader.name() == "header"; + m_currentTable << TableRow(); + } +} + +void QtXmlToSphinx::handleListTag(QXmlStreamReader& reader) +{ + // BUG We do not support a list inside a table cell + static QString listType; + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + listType = reader.attributes().value("type").toString(); + if (listType == "enum") { + m_currentTable << (TableRow() << "Constant" << "Description"); + m_tableHasHeader = true; + } + INDENT.indent--; + } else if (token == QXmlStreamReader::EndElement) { + INDENT.indent++; + if (!m_currentTable.isEmpty()) { + if (listType == "bullet") { + m_output << endl; + foreach (TableCell cell, m_currentTable.first()) { + QStringList itemLines = cell.data.split('\n'); + m_output << INDENT << "* " << itemLines.first() << endl; + for (int i = 1, max = itemLines.count(); i < max; ++i) + m_output << INDENT << " " << itemLines[i] << endl; + } + m_output << endl; + } else if (listType == "enum") { + m_currentTable.enableHeader(m_tableHasHeader); + m_currentTable.normalize(); + m_output << m_currentTable; + } + } + m_currentTable.clear(); + } +} + +void QtXmlToSphinx::handleLinkTag(QXmlStreamReader& reader) +{ + static QString l_linktag; + static QString l_linkref; + static QString l_linktext; + static QString l_linktagending; + static QString l_type; + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + l_linktagending = "` "; + if (m_insideBold) { + l_linktag.prepend("**"); + l_linktagending.append("**"); + } else if (m_insideItalic) { + l_linktag.prepend('*'); + l_linktagending.append('*'); + } + l_type = reader.attributes().value("type").toString(); + + // TODO: create a flag PROPERTY-AS-FUNCTION to ask if the properties + // are recognized as such or not in the binding + if (l_type == "property") + l_type = "function"; + + if (l_type == "typedef") + l_type = "class"; + + QString linkSource; + if (l_type == "function" || l_type == "class") { + linkSource = "raw"; + } else if (l_type == "enum") { + linkSource = "enum"; + } else if (l_type == "page") { + linkSource = "page"; + } else { + linkSource = "href"; + } + + l_linkref = reader.attributes().value(linkSource).toString(); + l_linkref.replace("::", "."); + l_linkref.remove("()"); + + if (l_type == "function" && !m_context.isEmpty()) { + l_linktag = " :meth:`"; + QStringList rawlinklist = l_linkref.split("."); + if (rawlinklist.size() == 1 || rawlinklist.first() == m_context) { + QString context = resolveContextForMethod(rawlinklist.last()); + if (!l_linkref.startsWith(context)) + l_linkref.prepend(context + '.'); + } else { + l_linkref = expandFunction(l_linkref); + } + } else if (l_type == "function" && m_context.isEmpty()) { + l_linktag = " :func:`"; + } else if (l_type == "class") { + l_linktag = " :class:`"; + TypeEntry* type = TypeDatabase::instance()->findType(l_linkref); + if (type) { + l_linkref = type->qualifiedTargetLangName(); + } else { // fall back to the old heuristic if the type wasn't found. + QStringList rawlinklist = l_linkref.split("."); + QStringList splittedContext = m_context.split("."); + if (rawlinklist.size() == 1 || rawlinklist.first() == splittedContext.last()) { + splittedContext.removeLast(); + l_linkref.prepend('~' + splittedContext.join(".") + '.'); + } + } + } else if (l_type == "enum") { + l_linktag = " :attr:`"; + } else if (l_type == "page" && l_linkref == m_generator->moduleName()) { + l_linktag = " :mod:`"; + } else { + l_linktag = " :ref:`"; + } + + } else if (token == QXmlStreamReader::Characters) { + QString linktext = reader.text().toString(); + linktext.replace("::", "."); + QString item = l_linkref.split(".").last(); + if (l_linkref == linktext + || (l_linkref + "()") == linktext + || item == linktext + || (item + "()") == linktext) + l_linktext.clear(); + else + l_linktext = linktext + QLatin1String("<"); + } else if (token == QXmlStreamReader::EndElement) { + if (!l_linktext.isEmpty()) + l_linktagending.prepend('>'); + m_output << l_linktag << l_linktext << escape(l_linkref) << l_linktagending; + } +} + +void QtXmlToSphinx::handleImageTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + QString href = reader.attributes().value("href").toString(); + QDir dir(m_generator->outputDirectory() + '/' + m_generator->packageName().replace(".", "/")); + QString imgPath = dir.relativeFilePath(m_generator->libSourceDir() + "/doc/src/") + '/' + href; + + if (reader.name() == "image") + m_output << INDENT << ".. image:: " << imgPath << endl << endl; + else + m_output << ".. image:: " << imgPath << ' '; + } +} + +void QtXmlToSphinx::handleRawTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + QString format = reader.attributes().value("format").toString(); + m_output << INDENT << ".. raw:: " << format.toLower() << endl << endl; + } else if (token == QXmlStreamReader::Characters) { + QStringList lst(reader.text().toString().split("\n")); + foreach(QString row, lst) + m_output << INDENT << INDENT << row << endl; + } else if (token == QXmlStreamReader::EndElement) { + m_output << endl << endl; + } +} + +void QtXmlToSphinx::handleCodeTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + QString format = reader.attributes().value("format").toString(); + m_output << INDENT << "::" << endl << endl; + INDENT.indent++; + } else if (token == QXmlStreamReader::Characters) { + QStringList lst(reader.text().toString().split("\n")); + foreach(QString row, lst) + m_output << INDENT << INDENT << row << endl; + } else if (token == QXmlStreamReader::EndElement) { + m_output << endl << endl; + INDENT.indent--; + } +} + +void QtXmlToSphinx::handleUnknownTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) + ReportHandler::warning("Unknow QtDoc tag: \"" + reader.name().toString() + "\"."); +} + +void QtXmlToSphinx::handleSuperScriptTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + m_output << " :sup:`"; + pushOutputBuffer(); + } else if (token == QXmlStreamReader::Characters) { + m_output << reader.text().toString(); + } else if (token == QXmlStreamReader::EndElement) { + m_output << popOutputBuffer(); + m_output << '`'; + } +} + +void QtXmlToSphinx::handleIgnoredTag(QXmlStreamReader&) +{ +} + +void QtXmlToSphinx::handleUselessTag(QXmlStreamReader&) +{ + // Tag "description" just marks the init of "Detailed description" title. + // Tag "definition" just marks enums. We have a different way to process them. +} + +void QtXmlToSphinx::handleAnchorTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + QString anchor; + if (reader.attributes().hasAttribute("id")) + anchor = reader.attributes().value("id").toString(); + else if (reader.attributes().hasAttribute("name")) + anchor = reader.attributes().value("name").toString(); + if (!anchor.isEmpty() && m_opened_anchor != anchor) { + m_opened_anchor = anchor; + m_output << INDENT << ".. _" << m_context << "_" << anchor.toLower() << ":" << endl << endl; + } + } else if (token == QXmlStreamReader::EndElement) { + m_opened_anchor = ""; + } +} + +void QtXmlToSphinx::handleQuoteFileTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::Characters) { + QString location = reader.text().toString(); + QString identifier = ""; + location.prepend(m_generator->libSourceDir() + '/'); + QString code = readFromLocation(location, identifier); + + m_output << INDENT << "::\n\n"; + Indentation indentation(INDENT); + if (code.isEmpty()) { + m_output << INDENT << "" << endl; + } else { + foreach (QString line, code.split("\n")) { + if (!QString(line).trimmed().isEmpty()) + m_output << INDENT << line; + + m_output << endl; + } + } + m_output << endl; + } +} + +void QtXmlToSphinx::Table::normalize() +{ + if (m_normalized || isEmpty()) + return; + + int row; + int col; + QtXmlToSphinx::Table& self = *this; + + //QDoc3 generates tables with wrong number of columns. We have to + //check and if necessary, merge the last columns. + int maxCols = self.at(0).count(); + // add col spans + for (row = 0; row < count(); ++row) { + for (col = 0; col < at(row).count(); ++col) { + QtXmlToSphinx::TableCell& cell = self[row][col]; + bool mergeCols = (col >= maxCols); + if (cell.colSpan > 0) { + QtXmlToSphinx::TableCell newCell; + newCell.colSpan = -1; + for (int i = 0, max = cell.colSpan-1; i < max; ++i) { + self[row].insert(col+1, newCell); + } + cell.colSpan = 0; + col++; + } else if (mergeCols) { + self[row][maxCols - 1].data += " " + cell.data; + } + } + } + + // row spans + const int numCols = first().count(); + for (col = 0; col < numCols; ++col) { + for (row = 0; row < count(); ++row) { + if (col < self[row].count()) { + QtXmlToSphinx::TableCell& cell = self[row][col]; + if (cell.rowSpan > 0) { + QtXmlToSphinx::TableCell newCell; + newCell.rowSpan = -1; + int max = std::min(cell.rowSpan - 1, count()); + cell.rowSpan = 0; + for (int i = 0; i < max; ++i) { + self[row+i+1].insert(col, newCell); + } + row++; + } + } + } + } + m_normalized = true; +} + +QTextStream& operator<<(QTextStream& s, const QtXmlToSphinx::Table &table) +{ + if (table.isEmpty()) + return s; + + if (!table.isNormalized()) { + ReportHandler::warning("Attempt to print an unnormalized table!"); + return s; + } + + // calc width and height of each column and row + QVector colWidths(table.first().count()); + QVector rowHeights(table.count()); + for (int i = 0, maxI = table.count(); i < maxI; ++i) { + const QtXmlToSphinx::TableRow& row = table[i]; + for (int j = 0, maxJ = std::min(row.count(), colWidths.size()); j < maxJ; ++j) { + QStringList rowLines = row[j].data.split('\n'); // cache this would be a good idea + foreach (QString str, rowLines) + colWidths[j] = std::max(colWidths[j], str.count()); + rowHeights[i] = std::max(rowHeights[i], row[j].data.count('\n') + 1); + } + } + + if (!*std::max_element(colWidths.begin(), colWidths.end())) + return s; // empty table (table with empty cells) + + // create a horizontal line to be used later. + QString horizontalLine("+"); + for (int i = 0, max = colWidths.count(); i < max; ++i) { + horizontalLine += createRepeatedChar(colWidths[i], '-'); + horizontalLine += '+'; + } + + // write table rows + for (int i = 0, maxI = table.count(); i < maxI; ++i) { // for each row + const QtXmlToSphinx::TableRow& row = table[i]; + + // print line + s << INDENT << '+'; + for (int col = 0, max = colWidths.count(); col < max; ++col) { + char c; + if (col >= row.length() || row[col].rowSpan == -1) + c = ' '; + else if (i == 1 && table.hasHeader()) + c = '='; + else + c = '-'; + s << createRepeatedChar(colWidths[col], c) << '+'; + } + s << endl; + + + // Print the table cells + for (int rowLine = 0; rowLine < rowHeights[i]; ++rowLine) { // for each line in a row + for (int j = 0, maxJ = std::min(row.count(), colWidths.size()); j < maxJ; ++j) { // for each column + const QtXmlToSphinx::TableCell& cell = row[j]; + QStringList rowLines = cell.data.split('\n'); // FIXME: Cache this!!! + if (!j) // First column, so we need print the identation + s << INDENT; + + if (!j || !cell.colSpan) + s << '|'; + else + s << ' '; + s << qSetFieldWidth(colWidths[j]) << left; + s << (rowLine < rowLines.count() ? rowLines[rowLine] : ""); + s << qSetFieldWidth(0); + } + s << '|' << endl; + } + } + s << INDENT << horizontalLine << endl; + s << endl; + return s; +} + +static QString getFuncName(const AbstractMetaFunction* cppFunc) { + static bool hashInitialized = false; + static QHash operatorsHash; + if (!hashInitialized) { + operatorsHash.insert("operator+", "__add__"); + operatorsHash.insert("operator+=", "__iadd__"); + operatorsHash.insert("operator-", "__sub__"); + operatorsHash.insert("operator-=", "__isub__"); + operatorsHash.insert("operator*", "__mul__"); + operatorsHash.insert("operator*=", "__imul__"); + operatorsHash.insert("operator/", "__div__"); + operatorsHash.insert("operator/=", "__idiv__"); + operatorsHash.insert("operator%", "__mod__"); + operatorsHash.insert("operator%=", "__imod__"); + operatorsHash.insert("operator<<", "__lshift__"); + operatorsHash.insert("operator<<=", "__ilshift__"); + operatorsHash.insert("operator>>", "__rshift__"); + operatorsHash.insert("operator>>=", "__irshift__"); + operatorsHash.insert("operator&", "__and__"); + operatorsHash.insert("operator&=", "__iand__"); + operatorsHash.insert("operator|", "__or__"); + operatorsHash.insert("operator|=", "__ior__"); + operatorsHash.insert("operator^", "__xor__"); + operatorsHash.insert("operator^=", "__ixor__"); + operatorsHash.insert("operator==", "__eq__"); + operatorsHash.insert("operator!=", "__ne__"); + operatorsHash.insert("operator<", "__lt__"); + operatorsHash.insert("operator<=", "__le__"); + operatorsHash.insert("operator>", "__gt__"); + operatorsHash.insert("operator>=", "__ge__"); + hashInitialized = true; + } + + QHash::const_iterator it = operatorsHash.find(cppFunc->name()); + QString result = it != operatorsHash.end() ? it.value() : cppFunc->name(); + return result.replace("::", "."); +} + +QtDocGenerator::QtDocGenerator() : m_docParser(0) +{ +} + +QtDocGenerator::~QtDocGenerator() +{ + delete m_docParser; +} + +QString QtDocGenerator::fileNameForClass(const AbstractMetaClass* cppClass) const +{ + return QString("%1.rst").arg(getClassTargetFullName(cppClass, false)); +} + +void QtDocGenerator::writeFormatedText(QTextStream& s, const Documentation& doc, const AbstractMetaClass* metaClass) +{ + QString metaClassName; + + if (metaClass) + metaClassName = getClassTargetFullName(metaClass); + + if (doc.format() == Documentation::Native) { + QtXmlToSphinx x(this, doc.value(), metaClassName); + s << x; + } else { + QStringList lines = doc.value().split("\n"); + QRegExp regex("\\S"); // non-space character + int typesystemIndentation = std::numeric_limits().max(); + // check how many spaces must be removed from the begining of each line + foreach (QString line, lines) { + int idx = line.indexOf(regex); + if (idx >= 0) + typesystemIndentation = qMin(typesystemIndentation, idx); + } + foreach (QString line, lines) + s << INDENT << line.remove(0, typesystemIndentation) << endl; + } + + s << endl; +} + +static void writeInheritedByList(QTextStream& s, const AbstractMetaClass* metaClass, const AbstractMetaClassList& allClasses) +{ + AbstractMetaClassList res; + foreach (AbstractMetaClass* c, allClasses) { + if (c != metaClass && c->inheritsFrom(metaClass)) + res << c; + } + + if (res.isEmpty()) + return; + + s << "**Inherited by:** "; + QStringList classes; + foreach (AbstractMetaClass* c, res) + classes << QString(":ref:`%1`").arg(getClassTargetFullName(c, false)); + s << classes.join(", ") << endl << endl; +} + +void QtDocGenerator::generateClass(QTextStream& s, const AbstractMetaClass* metaClass) +{ + ReportHandler::debugSparse("Generating Documentation for " + metaClass->fullName()); + + m_packages[metaClass->package()] << fileNameForClass(metaClass); + + m_docParser->setPackageName(metaClass->package()); + m_docParser->fillDocumentation(const_cast(metaClass)); + + s << ".. module:: " << metaClass->package() << endl; + QString className = getClassTargetFullName(metaClass, false); + s << ".. _" << className << ":" << endl << endl; + + s << className << endl; + s << createRepeatedChar(className.count(), '*') << endl << endl; + + s << ".. inheritance-diagram:: " << className << endl + << " :parts: 2" << endl << endl; // TODO: This would be a parameter in the future... + + + writeInheritedByList(s, metaClass, classes()); + + if (metaClass->typeEntry() && (metaClass->typeEntry()->version() != 0)) + s << ".. note:: This class was introduced in Qt " << metaClass->typeEntry()->version() << endl; + + writeFunctionList(s, metaClass); + + //Function list + AbstractMetaFunctionList functionList = metaClass->functions(); + qSort(functionList.begin(), functionList.end(), functionSort); + + s << "Detailed Description\n" + "--------------------\n\n"; + + writeInjectDocumentation(s, DocModification::Prepend, metaClass, 0); + if (!writeInjectDocumentation(s, DocModification::Replace, metaClass, 0)) + writeFormatedText(s, metaClass->documentation(), metaClass); + + if (!metaClass->isNamespace()) + writeConstructors(s, metaClass); + writeEnums(s, metaClass); + if (!metaClass->isNamespace()) + writeFields(s, metaClass); + + + foreach (AbstractMetaFunction* func, functionList) { + if (shouldSkip(func)) + continue; + + if (func->isStatic()) + s << ".. staticmethod:: "; + else + s << ".. method:: "; + + writeFunction(s, true, metaClass, func); + } + + writeInjectDocumentation(s, DocModification::Append, metaClass, 0); +} + +void QtDocGenerator::writeFunctionList(QTextStream& s, const AbstractMetaClass* cppClass) +{ + QStringList functionList; + QStringList virtualList; + QStringList signalList; + QStringList slotList; + QStringList staticFunctionList; + + foreach (AbstractMetaFunction* func, cppClass->functions()) { + if (shouldSkip(func)) + continue; + + QString className; + if (!func->isConstructor()) + className = getClassTargetFullName(cppClass) + '.'; + else if (func->implementingClass() && func->implementingClass()->enclosingClass()) + className = getClassTargetFullName(func->implementingClass()->enclosingClass()) + '.'; + QString funcName = getFuncName(func); + + QString str("def :meth:`"); + + str += funcName; + str += '<'; + if (!funcName.startsWith(className)) + str += className; + str += funcName; + str += ">` ("; + str += parseArgDocStyle(cppClass, func); + str += ')'; + + if (func->isStatic()) + staticFunctionList << str; + else if (func->isVirtual()) + virtualList << str; + else if (func->isSignal()) + signalList << str; + else if (func->isSlot()) + slotList << str; + else + functionList << str; + } + + if ((functionList.size() > 0) || (staticFunctionList.size() > 0)) { + QtXmlToSphinx::Table functionTable; + QtXmlToSphinx::TableRow row; + + s << "Synopsis" << endl + << "--------" << endl << endl; + + writeFunctionBlock(s, "Functions", functionList); + writeFunctionBlock(s, "Virtual functions", virtualList); + writeFunctionBlock(s, "Slots", slotList); + writeFunctionBlock(s, "Signals", signalList); + writeFunctionBlock(s, "Static functions", staticFunctionList); + } +} + +void QtDocGenerator::writeFunctionBlock(QTextStream& s, const QString& title, QStringList& functions) +{ + if (functions.size() > 0) { + s << title << endl + << QString('^').repeated(title.size()) << endl; + + qSort(functions); + + s << ".. container:: function_list" << endl << endl; + Indentation indentation(INDENT); + foreach (QString func, functions) + s << '*' << INDENT << func << endl; + + s << endl << endl; + } +} + +void QtDocGenerator::writeEnums(QTextStream& s, const AbstractMetaClass* cppClass) +{ + static const QString section_title(".. attribute:: "); + + foreach (AbstractMetaEnum* en, cppClass->enums()) { + s << section_title << getClassTargetFullName(cppClass) << "." << en->name() << endl << endl; + writeFormatedText(s, en->documentation(), cppClass); + + if (en->typeEntry() && (en->typeEntry()->version() != 0)) + s << ".. note:: This enum was introduced or modified in Qt " << en->typeEntry()->version() << endl; + } + +} + +void QtDocGenerator::writeFields(QTextStream& s, const AbstractMetaClass* cppClass) +{ + static const QString section_title(".. attribute:: "); + + foreach (AbstractMetaField* field, cppClass->fields()) { + s << section_title << getClassTargetFullName(cppClass) << "." << field->name() << endl << endl; + //TODO: request for member ‘documentation’ is ambiguous + writeFormatedText(s, field->AbstractMetaAttributes::documentation(), cppClass); + } +} + +void QtDocGenerator::writeConstructors(QTextStream& s, const AbstractMetaClass* cppClass) +{ + static const QString sectionTitle = ".. class:: "; + static const QString sectionTitleSpace = QString(sectionTitle.size(), ' '); + + AbstractMetaFunctionList lst = cppClass->queryFunctions(AbstractMetaClass::Constructors | AbstractMetaClass::Visible); + + bool first = true; + QHash arg_map; + + foreach(AbstractMetaFunction* func, lst) { + if (func->isModifiedRemoved()) + continue; + + if (first) { + first = false; + s << sectionTitle; + } else { + s << sectionTitleSpace; + } + writeFunction(s, false, cppClass, func); + foreach(AbstractMetaArgument* arg, func->arguments()) + { + if (!arg_map.contains(arg->name())) { + arg_map.insert(arg->name(), arg); + } + } + } + + s << endl; + + foreach (AbstractMetaArgument* arg, arg_map.values()) { + Indentation indentation(INDENT); + writeParamerteType(s, cppClass, arg); + } + + s << endl; + + foreach (AbstractMetaFunction* func, lst) { + writeFormatedText(s, func->documentation(), cppClass); + } +} + +QString QtDocGenerator::parseArgDocStyle(const AbstractMetaClass* cppClass, const AbstractMetaFunction* func) +{ + QString ret; + int optArgs = 0; + + foreach (AbstractMetaArgument* arg, func->arguments()) { + + if (func->argumentRemoved(arg->argumentIndex() + 1)) + continue; + + bool thisIsoptional = !arg->defaultValueExpression().isEmpty(); + if (optArgs || thisIsoptional) { + ret += '['; + optArgs++; + } + + if (arg->argumentIndex() > 0) + ret += ", "; + + ret += arg->name(); + + if (thisIsoptional) { + QString defValue = arg->defaultValueExpression(); + if (defValue == "QString()") { + defValue = "\"\""; + } else if (defValue == "QStringList()" || defValue.startsWith("QVector") || defValue.startsWith("QList")) { + defValue = "list()"; + } else if (defValue == "QVariant()") { + defValue = "None"; + } else { + defValue.replace("::", "."); + if (defValue == "0" && (arg->type()->isQObject() || arg->type()->isObject())) + defValue = "None"; + } + ret += "=" + defValue; + } + } + + ret += QString(']').repeated(optArgs); + return ret; +} + +void QtDocGenerator::writeDocSnips(QTextStream &s, + const CodeSnipList &codeSnips, + CodeSnip::Position position, + TypeSystem::Language language) +{ + Indentation indentation(INDENT); + QStringList invalidStrings; + const static QString startMarkup("[sphinx-begin]"); + const static QString endMarkup("[sphinx-end]"); + + invalidStrings << "*" << "//" << "/*" << "*/"; + + foreach (CodeSnip snip, codeSnips) { + if ((snip.position != position) || + !(snip.language & language)) + continue; + + QString code = snip.code(); + while (code.contains(startMarkup) && code.contains(endMarkup)) { + int startBlock = code.indexOf(startMarkup) + startMarkup.size(); + int endBlock = code.indexOf(endMarkup); + + if ((startBlock == -1) || (endBlock == -1)) + break; + + QString codeBlock = code.mid(startBlock, endBlock - startBlock); + QStringList rows = codeBlock.split("\n"); + int currenRow = 0; + int offset = 0; + + foreach(QString row, rows) { + foreach(QString invalidString, invalidStrings) { + row = row.remove(invalidString); + } + + if (row.trimmed().size() == 0) { + if (currenRow == 0) + continue; + else + s << endl; + } + + if (currenRow == 0) { + //find offset + for (int i=0, i_max = row.size(); i < i_max; i++) { + if (row[i] == ' ') + offset++; + else if (row[i] == '\n') + offset = 0; + else + break; + } + } + row = row.mid(offset); + s << row << endl; + currenRow++; + } + + code = code.mid(endBlock+endMarkup.size()); + } + } +} + +bool QtDocGenerator::writeInjectDocumentation(QTextStream& s, + DocModification::Mode mode, + const AbstractMetaClass* cppClass, + const AbstractMetaFunction* func) +{ + Indentation indentation(INDENT); + bool didSomething = false; + + foreach (DocModification mod, cppClass->typeEntry()->docModifications()) { + if (mod.mode() == mode) { + bool modOk = func ? mod.signature() == func->minimalSignature() : mod.signature().isEmpty(); + + if (modOk) { + Documentation doc; + Documentation::Format fmt; + + if (mod.format == TypeSystem::NativeCode) + fmt = Documentation::Native; + else if (mod.format == TypeSystem::TargetLangCode) + fmt = Documentation::Target; + else + continue; + + doc.setValue(mod.code() , fmt); + writeFormatedText(s, doc, cppClass); + didSomething = true; + } + } + } + + s << endl; + + // TODO: Deprecate the use of doc string on glue code. + // This is pre "add-function" and "inject-documentation" tags. + if (func) { + writeDocSnips(s, func->injectedCodeSnips(), + (mode == DocModification::Prepend ? CodeSnip::Beginning : CodeSnip::End), + TypeSystem::TargetLangCode); + } else { + writeDocSnips(s, cppClass->typeEntry()->codeSnips(), + (mode == DocModification::Prepend ? CodeSnip::Beginning : CodeSnip::End), + TypeSystem::TargetLangCode); + } + return didSomething; +} + +void QtDocGenerator::writeFunctionSignature(QTextStream& s, const AbstractMetaClass* cppClass, const AbstractMetaFunction* func) +{ + QString className; + if (!func->isConstructor()) + className = getClassTargetFullName(cppClass) + '.'; + else if (func->implementingClass() && func->implementingClass()->enclosingClass()) + className = getClassTargetFullName(func->implementingClass()->enclosingClass()) + '.'; + + QString funcName = getFuncName(func); + if (!funcName.startsWith(className)) + funcName = className + funcName; + + s << funcName << "(" << parseArgDocStyle(cppClass, func) << ")"; +} + +QString QtDocGenerator::translateToPythonType(const AbstractMetaType* type, const AbstractMetaClass* cppClass) +{ + QString strType; + if (type->name() == "QString") { + strType = "unicode"; + } else if (type->name() == "QVariant") { + strType = "object"; + } else if (type->name() == "QStringList") { + strType = "list of strings"; + } else if (type->isConstant() && type->name() == "char" && type->indirections() == 1) { + strType = "str"; + } else if (type->name().startsWith("unsigned short")) { + strType = "int"; + } else if (type->name().startsWith("unsigned ")) { // uint and ulong + strType = "long"; + } else if (type->isContainer()) { + QString strType = translateType(type, cppClass, Options(ExcludeConst) | ExcludeReference); + strType.remove("*"); + strType.remove(">"); + strType.remove("<"); + strType.replace("::", "."); + if (strType.contains("QList") || strType.contains("QVector")) { + strType.replace("QList", "list of "); + strType.replace("QVector", "list of "); + } else if (strType.contains("QHash") || strType.contains("QMap")) { + strType.remove("QHash"); + strType.remove("QMap"); + QStringList types = strType.split(","); + strType = QString("Dictionary with keys of type %1 and values of type %2.") + .arg(types[0]).arg(types[1]); + } + } else { + QString refTag; + if (type->isEnum()) + refTag = "attr"; + else + refTag = "class"; + strType = ':' + refTag + ":`" + type->fullName() + '`'; + } + return strType; +} + +void QtDocGenerator::writeParamerteType(QTextStream& s, const AbstractMetaClass* cppClass, const AbstractMetaArgument* arg) +{ + s << INDENT << ":param " << arg->name() << ": " + << translateToPythonType(arg->type(), cppClass) << endl; +} + +void QtDocGenerator::writeFunctionParametersType(QTextStream& s, const AbstractMetaClass* cppClass, const AbstractMetaFunction* func) +{ + Indentation indentation(INDENT); + + s << endl; + foreach (AbstractMetaArgument* arg, func->arguments()) { + + if (func->argumentRemoved(arg->argumentIndex() + 1)) + continue; + + writeParamerteType(s, cppClass, arg); + } + + if (!func->isConstructor() && func->type()) { + + QString retType; + // check if the return type was modified + foreach (FunctionModification mod, func->modifications()) { + foreach (ArgumentModification argMod, mod.argument_mods) { + if (argMod.index == 0) { + retType = argMod.modified_type; + break; + } + } + } + + if (retType.isEmpty()) + retType = translateToPythonType(func->type(), cppClass); + s << INDENT << ":rtype: " << retType << endl; + } + s << endl; +} + +void QtDocGenerator::writeFunction(QTextStream& s, bool writeDoc, const AbstractMetaClass* cppClass, const AbstractMetaFunction* func) +{ + writeFunctionSignature(s, cppClass, func); + s << endl; + + if (func->typeEntry() && (func->typeEntry()->version() != 0)) + s << ".. note:: This method was introduced in Qt " << func->typeEntry()->version() << endl; + + if (writeDoc) { + s << endl; + writeFunctionParametersType(s, cppClass, func); + s << endl; + writeInjectDocumentation(s, DocModification::Prepend, cppClass, func); + if (!writeInjectDocumentation(s, DocModification::Replace, cppClass, func)) + writeFormatedText(s, func->documentation(), cppClass); + writeInjectDocumentation(s, DocModification::Append, cppClass, func); + } +} + +static void writeFancyToc(QTextStream& s, const QStringList& items, int cols = 4) +{ + typedef QMap TocMap; + TocMap tocMap; + QChar Q('Q'); + QChar idx; + foreach (QString item, items) { + if (item.isEmpty()) + continue; + if (item.startsWith(Q) && item.length() > 1) + idx = item[1]; + item.chop(4); // Remove the .rst extension + tocMap[idx] << item; + } + QtXmlToSphinx::Table table; + QtXmlToSphinx::TableRow row; + + int itemsPerCol = (items.size() + tocMap.size()*2) / cols; + QString currentColData; + int i = 0; + QTextStream ss(¤tColData); + QMutableMapIterator it(tocMap); + while (it.hasNext()) { + it.next(); + qSort(it.value()); + + if (i) + ss << endl; + + ss << "**" << it.key() << "**" << endl << endl; + i += 2; // a letter title is equivalent to two entries in space + foreach (QString item, it.value()) { + ss << "* :doc:`" << item << "`" << endl; + ++i; + + // end of column detected! + if (i > itemsPerCol) { + ss.flush(); + QtXmlToSphinx::TableCell cell(currentColData); + row << cell; + currentColData.clear(); + i = 0; + } + } + } + if (i) { + ss.flush(); + QtXmlToSphinx::TableCell cell(currentColData); + row << cell; + currentColData.clear(); + i = 0; + } + table << row; + table.normalize(); + s << ".. container:: pysidetoc" << endl << endl; + s << table; +} + +void QtDocGenerator::finishGeneration() +{ + if (classes().isEmpty()) + return; + + QMap::iterator it = m_packages.begin(); + for (; it != m_packages.end(); ++it) { + QString outputDir = outputDirectory() + '/' + QString(it.key()).replace(".", "/"); + FileOut output(outputDir + "/index.rst"); + QTextStream& s = output.stream; + + s << ".. module:: " << it.key() << endl << endl; + + QString title = it.key(); + s << title << endl; + s << createRepeatedChar(title.length(), '*') << endl << endl; + + /* Avoid showing "Detailed Description for *every* class in toc tree */ + Indentation indentation(INDENT); + + // Search for extra-sections + if (!m_extraSectionDir.isEmpty()) { + QDir extraSectionDir(m_extraSectionDir); + QStringList fileList = extraSectionDir.entryList(QStringList() << (it.key() + "?*.rst"), QDir::Files); + QStringList::iterator it2 = fileList.begin(); + for (; it2 != fileList.end(); ++it2) { + QString origFileName(*it2); + it2->remove(0, it.key().count() + 1); + QString newFilePath = outputDir + '/' + *it2; + if (QFile::exists(newFilePath)) + QFile::remove(newFilePath); + if (!QFile::copy(m_extraSectionDir + '/' + origFileName, newFilePath)) { + ReportHandler::warning("Error copying extra doc " + (m_extraSectionDir + '/' + origFileName) + + " to " + newFilePath); + } + } + it.value().append(fileList); + } + + writeFancyToc(s, it.value()); + + s << INDENT << ".. container:: hide" << endl << endl; + { + Indentation indentation(INDENT); + s << INDENT << ".. toctree::" << endl; + Indentation deeperIndentation(INDENT); + s << INDENT << ":maxdepth: 1" << endl << endl; + foreach (QString className, it.value()) + s << INDENT << className << endl; + s << endl << endl; + } + + s << "Detailed Description" << endl; + s << "--------------------" << endl << endl; + + // module doc is always wrong and C++istic, so go straight to the extra directory! + QFile moduleDoc(m_extraSectionDir + '/' + it.key() + ".rst"); + if (moduleDoc.open(QIODevice::ReadOnly | QIODevice::Text)) { + s << moduleDoc.readAll(); + moduleDoc.close(); + } else { + // try the normal way + Documentation moduleDoc = m_docParser->retrieveModuleDocumentation(it.key()); + if (moduleDoc.format() == Documentation::Native) { + QtXmlToSphinx x(this, moduleDoc.value(), QString(it.key()).remove(0, it.key().lastIndexOf('.') + 1)); + s << x; + } else { + s << moduleDoc.value(); + } + } + } +} + +bool QtDocGenerator::doSetup(const QMap& args) +{ + m_libSourceDir = args.value("library-source-dir"); + m_docDataDir = args.value("documentation-data-dir"); +#ifdef __WIN32__ +# define PATH_SEP ";" +#else +# define PATH_SEP ":" +#endif + m_codeSnippetDirs = args.value("documentation-code-snippets-dir", m_libSourceDir).split(PATH_SEP); + m_extraSectionDir = args.value("documentation-extra-sections-dir"); + + m_docParser = args.value("doc-parser") == "doxygen" ? reinterpret_cast(new DoxygenParser) : reinterpret_cast(new QtDocParser); + ReportHandler::warning("doc-parser: " + args.value("doc-parser")); + + if (m_libSourceDir.isEmpty() || m_docDataDir.isEmpty()) { + ReportHandler::warning("Documentation data dir and/or Qt source dir not informed, " + "documentation will not be extracted from Qt sources."); + return false; + } else { + m_docParser->setDocumentationDataDirectory(m_docDataDir); + m_docParser->setLibrarySourceDirectory(m_libSourceDir); + } + return true; +} + + +QMap QtDocGenerator::options() const +{ + QMap options; + options.insert("doc-parser", "The documentation parser used to interpret the documentaion input files (qdoc3|doxygen)"); + options.insert("library-source-dir", "Directory where library source code is located"); + options.insert("documentation-data-dir", "Directory with XML files generated by documentation tool (qdoc3 or Doxygen)"); + options.insert("documentation-code-snippets-dir", "Directory used to search code snippets used by the documentation"); + options.insert("documentation-extra-sections-dir", "Directory used to search for extra documentation sections"); + return options; +} + diff --git a/generator/qtdoc/qtdocgenerator.h b/generator/qtdoc/qtdocgenerator.h new file mode 100644 index 000000000..247b48363 --- /dev/null +++ b/generator/qtdoc/qtdocgenerator.h @@ -0,0 +1,226 @@ +/* + * This file is part of the PySide project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ +#ifndef DOCGENERATOR_H +#define DOCGENERATOR_H + +#include +#include +#include +#include +#include +#include "generator.h" +#include "docparser.h" + +class QtDocParser; +class AbstractMetaFunction; +class AbstractMetaClass; +class QXmlStreamReader; +class QtDocGenerator; + +class QtXmlToSphinx +{ +public: + struct TableCell + { + short rowSpan; + short colSpan; + QString data; + + TableCell(const QString& text = QString()) : rowSpan(0), colSpan(0), data(text) {} + TableCell(const char* text) : rowSpan(0), colSpan(0), data(text) {} + }; + + typedef QList TableRow; + class Table : public QList + { + public: + Table() : m_hasHeader(false), m_normalized(false) + { + } + + void enableHeader(bool enable) + { + m_hasHeader = enable; + } + + bool hasHeader() const + { + return m_hasHeader; + } + + void normalize(); + + bool isNormalized() const + { + return m_normalized; + } + + void clear() { + m_normalized = false; + QList::clear(); + } + + private: + bool m_hasHeader; + bool m_normalized; + }; + + QtXmlToSphinx(QtDocGenerator* generator, const QString& doc, const QString& context = QString()); + + QString result() const + { + return m_result; + } + +private: + QString resolveContextForMethod(const QString& methodName); + QString expandFunction(const QString& function); + QString transform(const QString& doc); + + void handleHeadingTag(QXmlStreamReader& reader); + void handleParaTag(QXmlStreamReader& reader); + void handleItalicTag(QXmlStreamReader& reader); + void handleBoldTag(QXmlStreamReader& reader); + void handleArgumentTag(QXmlStreamReader& reader); + void handleSeeAlsoTag(QXmlStreamReader& reader); + void handleSnippetTag(QXmlStreamReader& reader); + void handleDotsTag(QXmlStreamReader& reader); + void handleLinkTag(QXmlStreamReader& reader); + void handleImageTag(QXmlStreamReader& reader); + void handleListTag(QXmlStreamReader& reader); + void handleTermTag(QXmlStreamReader& reader); + void handleSuperScriptTag(QXmlStreamReader& reader); + void handleQuoteFileTag(QXmlStreamReader& reader); + + // table tagsvoid QtXmlToSphinx::handleValueTag(QXmlStreamReader& reader) + + void handleTableTag(QXmlStreamReader& reader); + void handleRowTag(QXmlStreamReader& reader); + void handleItemTag(QXmlStreamReader& reader); + void handleRawTag(QXmlStreamReader& reader); + void handleCodeTag(QXmlStreamReader& reader); + + void handleIgnoredTag(QXmlStreamReader& reader); + void handleUnknownTag(QXmlStreamReader& reader); + void handleUselessTag(QXmlStreamReader& reader); + void handleAnchorTag(QXmlStreamReader& reader); + + typedef void (QtXmlToSphinx::*TagHandler)(QXmlStreamReader&); + QHash m_handlerMap; + QStack m_handlers; + QTextStream m_output; + QString m_result; + + QStack m_buffers; + + + Table m_currentTable; + bool m_tableHasHeader; + QString m_context; + QtDocGenerator* m_generator; + bool m_insideBold; + bool m_insideItalic; + QString m_lastTagName; + QString m_opened_anchor; + + QString readFromLocations(const QStringList& locations, const QString& path, const QString& identifier); + QString readFromLocation(const QString& location, const QString& identifier, bool* ok = 0); + void pushOutputBuffer(); + QString popOutputBuffer(); + void writeTable(Table& table); +}; + +inline QTextStream& operator<<(QTextStream& s, const QtXmlToSphinx& xmlToSphinx) +{ + return s << xmlToSphinx.result(); +} + +QTextStream& operator<<(QTextStream& s, const QtXmlToSphinx::Table &table); + +/** +* The DocGenerator generates documentation from library being binded. +*/ +class QtDocGenerator : public Generator +{ +public: + QtDocGenerator(); + ~QtDocGenerator(); + + QString libSourceDir() const + { + return m_libSourceDir; + } + + bool doSetup(const QMap& args); + + const char* name() const + { + return "QtDocGenerator"; + } + + QMap options() const; + + QStringList codeSnippetDirs() const + { + return m_codeSnippetDirs; + } + +protected: + QString fileNameForClass(const AbstractMetaClass* cppClass) const; + void generateClass(QTextStream& s, const AbstractMetaClass* metaClass); + void finishGeneration(); + + void writeFunctionArguments(QTextStream&, const AbstractMetaFunction*, Options) const {} + void writeArgumentNames(QTextStream&, const AbstractMetaFunction*, Options) const {} + +private: + void writeEnums(QTextStream& s, const AbstractMetaClass* cppClass); + + void writeFields(QTextStream &s, const AbstractMetaClass *cppClass); + void writeArguments(QTextStream &s, const AbstractMetaClass *cppClass, const AbstractMetaFunction *func); + void writeFunctionSignature(QTextStream& s, const AbstractMetaClass* cppClass, const AbstractMetaFunction* func); + void writeFunction(QTextStream& s, bool writeDoc, const AbstractMetaClass* cppClass, const AbstractMetaFunction* func); + void writeFunctionParametersType(QTextStream &s, const AbstractMetaClass *cppClass, const AbstractMetaFunction* func); + void writeFunctionList(QTextStream& s, const AbstractMetaClass* cppClass); + void writeFunctionBlock(QTextStream& s, const QString& title, QStringList& functions); + void writeParamerteType(QTextStream &s, const AbstractMetaClass *cppClass, const AbstractMetaArgument *arg); + + void writeConstructors(QTextStream &s, const AbstractMetaClass *cppClass); + void writeFormatedText(QTextStream& s, const Documentation& doc, const AbstractMetaClass* metaclass = 0); + bool writeInjectDocumentation(QTextStream& s, DocModification::Mode mode, const AbstractMetaClass* cppClass, const AbstractMetaFunction* func); + void writeDocSnips(QTextStream &s, const CodeSnipList &codeSnips, CodeSnip::Position position, TypeSystem::Language language); + + + QString parseArgDocStyle(const AbstractMetaClass *cppClass, const AbstractMetaFunction *func); + QString translateToPythonType(const AbstractMetaType *type, const AbstractMetaClass *cppClass); + + QString m_docDataDir; + QString m_libSourceDir; + QStringList m_codeSnippetDirs; + QString m_extraSectionDir; + QStringList m_functionList; + QMap m_packages; + DocParser* m_docParser; +}; + +#endif // DOCGENERATOR_H diff --git a/generator/shiboken/CMakeLists.txt b/generator/shiboken/CMakeLists.txt new file mode 100644 index 000000000..57aac33ad --- /dev/null +++ b/generator/shiboken/CMakeLists.txt @@ -0,0 +1,30 @@ +project(shibokengenerator) + +set(shiboken_SRC +../generator.cpp +cppgenerator.cpp +headergenerator.cpp +overloaddata.cpp +shibokengenerator.cpp +shibokennormalize.cpp +main.cpp +) + +include_directories(${generators_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} + ${APIEXTRACTOR_INCLUDE_DIR} + ${QT_INCLUDE_DIR} + ${QT_QTCORE_INCLUDE_DIR} + ${QT_QTXML_INCLUDE_DIR}) + +add_executable(shiboken ${shiboken_SRC}) +set_target_properties(shiboken PROPERTIES OUTPUT_NAME shiboken${shiboken_SUFFIX}) +target_link_libraries(shiboken + ${APIEXTRACTOR_LIBRARY} + ${QT_QTCORE_LIBRARY} + ${QT_QTXML_LIBRARY}) + +configure_file(shibokenconfig.h.in "${CMAKE_CURRENT_BINARY_DIR}/shibokenconfig.h" @ONLY) + +install(TARGETS shiboken DESTINATION bin) diff --git a/generator/shiboken/cppgenerator.cpp b/generator/shiboken/cppgenerator.cpp new file mode 100644 index 000000000..fa19f7a3d --- /dev/null +++ b/generator/shiboken/cppgenerator.cpp @@ -0,0 +1,5125 @@ +/* + * This file is part of the Shiboken Python Bindings Generator project. + * + * Copyright (C) 2009-2012 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "cppgenerator.h" +#include "shibokennormalize_p.h" +#include +#include + +#include +#include +#include +#include + +QHash CppGenerator::m_nbFuncs = QHash(); +QHash CppGenerator::m_sqFuncs = QHash(); +QHash CppGenerator::m_mpFuncs = QHash(); +QString CppGenerator::m_currentErrorCode("0"); + +// utility functions +inline AbstractMetaType* getTypeWithoutContainer(AbstractMetaType* arg) +{ + if (arg && arg->typeEntry()->isContainer()) { + AbstractMetaTypeList lst = arg->instantiations(); + // only support containers with 1 type + if (lst.size() == 1) + return lst[0]; + } + return arg; +} + +CppGenerator::CppGenerator() +{ + // Number protocol structure members names + m_nbFuncs["__add__"] = "nb_add"; + m_nbFuncs["__sub__"] = "nb_subtract"; + m_nbFuncs["__mul__"] = "nb_multiply"; + m_nbFuncs["__div__"] = "nb_divide"; + m_nbFuncs["__mod__"] = "nb_remainder"; + m_nbFuncs["__neg__"] = "nb_negative"; + m_nbFuncs["__pos__"] = "nb_positive"; + m_nbFuncs["__invert__"] = "nb_invert"; + m_nbFuncs["__lshift__"] = "nb_lshift"; + m_nbFuncs["__rshift__"] = "nb_rshift"; + m_nbFuncs["__and__"] = "nb_and"; + m_nbFuncs["__xor__"] = "nb_xor"; + m_nbFuncs["__or__"] = "nb_or"; + m_nbFuncs["__iadd__"] = "nb_inplace_add"; + m_nbFuncs["__isub__"] = "nb_inplace_subtract"; + m_nbFuncs["__imul__"] = "nb_multiply"; + m_nbFuncs["__idiv__"] = "nb_divide"; + m_nbFuncs["__imod__"] = "nb_remainder"; + m_nbFuncs["__ilshift__"] = "nb_inplace_lshift"; + m_nbFuncs["__irshift__"] = "nb_inplace_rshift"; + m_nbFuncs["__iand__"] = "nb_inplace_and"; + m_nbFuncs["__ixor__"] = "nb_inplace_xor"; + m_nbFuncs["__ior__"] = "nb_inplace_or"; + m_nbFuncs["bool"] = "nb_nonzero"; + + // sequence protocol functions + typedef QPair StrPair; + m_sequenceProtocol.insert("__len__", StrPair("PyObject* " PYTHON_SELF_VAR, "Py_ssize_t")); + m_sequenceProtocol.insert("__getitem__", StrPair("PyObject* " PYTHON_SELF_VAR ", Py_ssize_t _i", "PyObject*")); + m_sequenceProtocol.insert("__setitem__", StrPair("PyObject* " PYTHON_SELF_VAR ", Py_ssize_t _i, PyObject* _value", "int")); + m_sequenceProtocol.insert("__getslice__", StrPair("PyObject* " PYTHON_SELF_VAR ", Py_ssize_t _i1, Py_ssize_t _i2", "PyObject*")); + m_sequenceProtocol.insert("__setslice__", StrPair("PyObject* " PYTHON_SELF_VAR ", Py_ssize_t _i1, Py_ssize_t _i2, PyObject* _value", "int")); + m_sequenceProtocol.insert("__contains__", StrPair("PyObject* " PYTHON_SELF_VAR ", PyObject* _value", "int")); + m_sequenceProtocol.insert("__concat__", StrPair("PyObject* " PYTHON_SELF_VAR ", PyObject* _other", "PyObject*")); + + // Sequence protocol structure members names + m_sqFuncs["__concat__"] = "sq_concat"; + m_sqFuncs["__contains__"] = "sq_contains"; + m_sqFuncs["__getitem__"] = "sq_item"; + m_sqFuncs["__getslice__"] = "sq_slice"; + m_sqFuncs["__len__"] = "sq_length"; + m_sqFuncs["__setitem__"] = "sq_ass_item"; + m_sqFuncs["__setslice__"] = "sq_ass_slice"; + + // mapping protocol function + m_mappingProtocol.insert("__mlen__", StrPair("PyObject* " PYTHON_SELF_VAR, "Py_ssize_t")); + m_mappingProtocol.insert("__mgetitem__", StrPair("PyObject* " PYTHON_SELF_VAR ", PyObject* _key", "PyObject*")); + m_mappingProtocol.insert("__msetitem__", StrPair("PyObject* " PYTHON_SELF_VAR ", PyObject* _key, PyObject* _value", "int")); + + // Sequence protocol structure members names + m_mpFuncs["__mlen__"] = "mp_length"; + m_mpFuncs["__mgetitem__"] = "mp_subscript"; + m_mpFuncs["__msetitem__"] = "mp_ass_subscript"; +} + +QString CppGenerator::fileNameForClass(const AbstractMetaClass *metaClass) const +{ + return metaClass->qualifiedCppName().toLower().replace("::", "_") + QLatin1String("_wrapper.cpp"); +} + +QList CppGenerator::filterGroupedOperatorFunctions(const AbstractMetaClass* metaClass, + uint query) +{ + // ( func_name, num_args ) => func_list + QMap, AbstractMetaFunctionList> results; + foreach (AbstractMetaFunction* func, metaClass->operatorOverloads(query)) { + if (func->isModifiedRemoved() || func->name() == "operator[]" || func->name() == "operator->") + continue; + int args; + if (func->isComparisonOperator()) { + args = -1; + } else { + args = func->arguments().size(); + } + QPair op(func->name(), args); + results[op].append(func); + } + return results.values(); +} + +bool CppGenerator::hasBoolCast(const AbstractMetaClass* metaClass) const +{ + if (!useIsNullAsNbNonZero()) + return false; + // TODO: This could be configurable someday + const AbstractMetaFunction* func = metaClass->findFunction("isNull"); + if (!func || !func->type() || !func->type()->typeEntry()->isPrimitive() || !func->isPublic()) + return false; + const PrimitiveTypeEntry* pte = static_cast(func->type()->typeEntry()); + while (pte->aliasedTypeEntry()) + pte = pte->aliasedTypeEntry(); + return func && func->isConstant() && pte->name() == "bool" && func->arguments().isEmpty(); +} + +/*! + Function used to write the class generated binding code on the buffer + \param s the output buffer + \param metaClass the pointer to metaclass information +*/ +void CppGenerator::generateClass(QTextStream &s, const AbstractMetaClass *metaClass) +{ + ReportHandler::debugSparse("Generating wrapper implementation for " + metaClass->fullName()); + + // write license comment + s << licenseComment() << endl; + + if (!avoidProtectedHack() && !metaClass->isNamespace() && !metaClass->hasPrivateDestructor()) { + s << "//workaround to access protected functions" << endl; + s << "#define protected public" << endl << endl; + } + + // headers + s << "// default includes" << endl; + s << "#include " << endl; + if (usePySideExtensions()) { + s << "#include " << endl; + s << "#include " << endl; + s << "#include " << endl; + s << "#include " << endl; + } + + s << "#include " << endl; + s << "#include " << endl; + if (usePySideExtensions() && metaClass->isQObject()) { + s << "#include " << endl; + s << "#include " << endl; + } + + // The multiple inheritance initialization function + // needs the 'set' class from C++ STL. + if (hasMultipleInheritanceInAncestry(metaClass)) + s << "#include " << endl; + + s << "#include \"" << getModuleHeaderFileName() << '"' << endl << endl; + + QString headerfile = fileNameForClass(metaClass); + headerfile.replace(".cpp", ".h"); + s << "#include \"" << headerfile << '"' << endl; + foreach (AbstractMetaClass* innerClass, metaClass->innerClasses()) { + if (shouldGenerate(innerClass)) { + QString headerfile = fileNameForClass(innerClass); + headerfile.replace(".cpp", ".h"); + s << "#include \"" << headerfile << '"' << endl; + } + } + + AbstractMetaEnumList classEnums = metaClass->enums(); + foreach (AbstractMetaClass* innerClass, metaClass->innerClasses()) + lookForEnumsInClassesNotToBeGenerated(classEnums, innerClass); + + //Extra includes + s << endl << "// Extra includes" << endl; + QList includes = metaClass->typeEntry()->extraIncludes(); + foreach (AbstractMetaEnum* cppEnum, classEnums) + includes.append(cppEnum->typeEntry()->extraIncludes()); + qSort(includes.begin(), includes.end()); + foreach (Include inc, includes) + s << inc.toString() << endl; + s << endl; + + if (metaClass->typeEntry()->typeFlags() & ComplexTypeEntry::Deprecated) + s << "#Deprecated" << endl; + + //Use class base namespace + const AbstractMetaClass *context = metaClass->enclosingClass(); + while(context) { + if (context->isNamespace() && !context->enclosingClass()) { + s << "using namespace " << context->qualifiedCppName() << ";" << endl; + break; + } + context = context->enclosingClass(); + } + + s << endl; + + // class inject-code native/beginning + if (!metaClass->typeEntry()->codeSnips().isEmpty()) { + writeCodeSnips(s, metaClass->typeEntry()->codeSnips(), CodeSnip::Beginning, TypeSystem::NativeCode, metaClass); + s << endl; + } + + // python conversion rules + if (metaClass->typeEntry()->hasTargetConversionRule()) { + s << "// Python Conversion" << endl; + s << metaClass->typeEntry()->conversionRule() << endl; + } + + if (shouldGenerateCppWrapper(metaClass)) { + s << "// Native ---------------------------------------------------------" << endl; + s << endl; + + if (avoidProtectedHack() && usePySideExtensions()) { + s << "void " << wrapperName(metaClass) << "::pysideInitQtMetaTypes()\n{\n"; + Indentation indent(INDENT); + writeInitQtMetaTypeFunctionBody(s, metaClass); + s << "}\n\n"; + } + + foreach (const AbstractMetaFunction* func, filterFunctions(metaClass)) { + if ((func->isPrivate() && !visibilityModifiedToPrivate(func)) + || (func->isModifiedRemoved() && !func->isAbstract())) + continue; + if (func->isConstructor() && !func->isCopyConstructor() && !func->isUserAdded()) + writeConstructorNative(s, func); + else if ((!avoidProtectedHack() || !metaClass->hasPrivateDestructor()) + && (func->isVirtual() || func->isAbstract())) + writeVirtualMethodNative(s, func); + } + + if (!avoidProtectedHack() || !metaClass->hasPrivateDestructor()) { + if (usePySideExtensions() && metaClass->isQObject()) + writeMetaObjectMethod(s, metaClass); + writeDestructorNative(s, metaClass); + } + } + + Indentation indentation(INDENT); + + QString methodsDefinitions; + QTextStream md(&methodsDefinitions); + QString singleMethodDefinitions; + QTextStream smd(&singleMethodDefinitions); + + s << endl << "// Target ---------------------------------------------------------" << endl << endl; + s << "extern \"C\" {" << endl; + foreach (AbstractMetaFunctionList allOverloads, getFunctionGroups(metaClass).values()) { + AbstractMetaFunctionList overloads; + foreach (AbstractMetaFunction* func, allOverloads) { + if (!func->isAssignmentOperator() + && !func->isCastOperator() + && !func->isModifiedRemoved() + && (!func->isPrivate() || func->functionType() == AbstractMetaFunction::EmptyFunction) + && func->ownerClass() == func->implementingClass() + && (func->name() != "qt_metacall")) + overloads.append(func); + } + + if (overloads.isEmpty()) + continue; + + const AbstractMetaFunction* rfunc = overloads.first(); + if (m_sequenceProtocol.contains(rfunc->name()) || m_mappingProtocol.contains(rfunc->name())) + continue; + + if (rfunc->isConstructor()) + writeConstructorWrapper(s, overloads); + // call operators + else if (rfunc->name() == "operator()") + writeMethodWrapper(s, overloads); + else if (!rfunc->isOperatorOverload()) { + writeMethodWrapper(s, overloads); + if (OverloadData::hasStaticAndInstanceFunctions(overloads)) { + QString methDefName = cpythonMethodDefinitionName(rfunc); + smd << "static PyMethodDef " << methDefName << " = {" << endl; + smd << INDENT; + writeMethodDefinitionEntry(smd, overloads); + smd << endl << "};" << endl << endl; + } + writeMethodDefinition(md, overloads); + } + } + + QString className = cpythonTypeName(metaClass).replace(QRegExp("_Type$"), ""); + + if (metaClass->typeEntry()->isValue()) + writeCopyFunction(s, metaClass); + + // Write single method definitions + s << singleMethodDefinitions; + + // Write methods definition + s << "static PyMethodDef " << className << "_methods[] = {" << endl; + s << methodsDefinitions << endl; + if (metaClass->typeEntry()->isValue()) + s << INDENT << "{\"__copy__\", (PyCFunction)" << className << "___copy__" << ", METH_NOARGS}," << endl; + s << INDENT << "{0} // Sentinel" << endl; + s << "};" << endl << endl; + + // Write tp_getattro function + if (usePySideExtensions() && metaClass->qualifiedCppName() == "QObject") { + writeGetattroFunction(s, metaClass); + s << endl; + writeSetattroFunction(s, metaClass); + s << endl; + } else if (classNeedsGetattroFunction(metaClass)) { + writeGetattroFunction(s, metaClass); + s << endl; + } + + if (hasBoolCast(metaClass)) { + ErrorCode errorCode(-1); + s << "static int " << cpythonBaseName(metaClass) << "___nb_bool(PyObject* " PYTHON_SELF_VAR ")" << endl; + s << '{' << endl; + writeCppSelfDefinition(s, metaClass); + s << INDENT << "int result;" << endl; + s << INDENT << BEGIN_ALLOW_THREADS << endl; + s << INDENT << "result = !" CPP_SELF_VAR "->isNull();" << endl; + s << INDENT << END_ALLOW_THREADS << endl; + s << INDENT << "return result;" << endl; + s << '}' << endl << endl; + } + + if (supportsNumberProtocol(metaClass)) { + QList opOverloads = filterGroupedOperatorFunctions( + metaClass, + AbstractMetaClass::ArithmeticOp + | AbstractMetaClass::LogicalOp + | AbstractMetaClass::BitwiseOp); + + foreach (AbstractMetaFunctionList allOverloads, opOverloads) { + AbstractMetaFunctionList overloads; + foreach (AbstractMetaFunction* func, allOverloads) { + if (!func->isModifiedRemoved() + && !func->isPrivate() + && (func->ownerClass() == func->implementingClass() || func->isAbstract())) + overloads.append(func); + } + + if (overloads.isEmpty()) + continue; + + writeMethodWrapper(s, overloads); + } + } + + if (supportsSequenceProtocol(metaClass)) { + writeSequenceMethods(s, metaClass); + } + + if (supportsMappingProtocol(metaClass)) { + writeMappingMethods(s, metaClass); + } + + if (metaClass->hasComparisonOperatorOverload()) { + s << "// Rich comparison" << endl; + writeRichCompareFunction(s, metaClass); + } + + if (shouldGenerateGetSetList(metaClass)) { + foreach (const AbstractMetaField* metaField, metaClass->fields()) { + if (metaField->isStatic()) + continue; + writeGetterFunction(s, metaField); + if (!metaField->type()->isConstant()) + writeSetterFunction(s, metaField); + s << endl; + } + + s << "// Getters and Setters for " << metaClass->name() << endl; + s << "static PyGetSetDef " << cpythonGettersSettersDefinitionName(metaClass) << "[] = {" << endl; + foreach (const AbstractMetaField* metaField, metaClass->fields()) { + if (metaField->isStatic()) + continue; + + bool hasSetter = !metaField->type()->isConstant(); + s << INDENT << "{const_cast(\"" << metaField->name() << "\"), "; + s << cpythonGetterFunctionName(metaField); + s << ", " << (hasSetter ? cpythonSetterFunctionName(metaField) : "0"); + s << "}," << endl; + } + s << INDENT << "{0} // Sentinel" << endl; + s << "};" << endl << endl; + } + + s << "} // extern \"C\"" << endl << endl; + + if (!metaClass->typeEntry()->hashFunction().isEmpty()) + writeHashFunction(s, metaClass); + + // Write tp_traverse and tp_clear functions. + writeTpTraverseFunction(s, metaClass); + writeTpClearFunction(s, metaClass); + + writeClassDefinition(s, metaClass); + s << endl; + + if (metaClass->isPolymorphic() && metaClass->baseClass()) + writeTypeDiscoveryFunction(s, metaClass); + + + foreach (AbstractMetaEnum* cppEnum, classEnums) { + if (cppEnum->isAnonymous() || cppEnum->isPrivate()) + continue; + + bool hasFlags = cppEnum->typeEntry()->flags(); + if (hasFlags) { + writeFlagsMethods(s, cppEnum); + writeFlagsNumberMethodsDefinition(s, cppEnum); + s << endl; + } + } + s << endl; + + writeConverterFunctions(s, metaClass); + writeClassRegister(s, metaClass); + + // class inject-code native/end + if (!metaClass->typeEntry()->codeSnips().isEmpty()) { + writeCodeSnips(s, metaClass->typeEntry()->codeSnips(), CodeSnip::End, TypeSystem::NativeCode, metaClass); + s << endl; + } +} + +void CppGenerator::writeConstructorNative(QTextStream& s, const AbstractMetaFunction* func) +{ + Indentation indentation(INDENT); + s << functionSignature(func, wrapperName(func->ownerClass()) + "::", "", + OriginalTypeDescription | SkipDefaultValues); + s << " : "; + writeFunctionCall(s, func); + s << " {" << endl; + const AbstractMetaArgument* lastArg = func->arguments().isEmpty() ? 0 : func->arguments().last(); + writeCodeSnips(s, func->injectedCodeSnips(), CodeSnip::Beginning, TypeSystem::NativeCode, func, lastArg); + s << INDENT << "// ... middle" << endl; + writeCodeSnips(s, func->injectedCodeSnips(), CodeSnip::End, TypeSystem::NativeCode, func, lastArg); + s << '}' << endl << endl; +} + +void CppGenerator::writeDestructorNative(QTextStream &s, const AbstractMetaClass *metaClass) +{ + Indentation indentation(INDENT); + s << wrapperName(metaClass) << "::~" << wrapperName(metaClass) << "()" << endl << '{' << endl; + // kill pyobject + s << INDENT << "SbkObject* wrapper = Shiboken::BindingManager::instance().retrieveWrapper(this);" << endl; + s << INDENT << "Shiboken::Object::destroy(wrapper, this);" << endl; + s << '}' << endl; +} + +static bool allArgumentsRemoved(const AbstractMetaFunction* func) +{ + if (func->arguments().isEmpty()) + return false; + foreach (const AbstractMetaArgument* arg, func->arguments()) { + if (!func->argumentRemoved(arg->argumentIndex() + 1)) + return false; + } + return true; +} + +QString CppGenerator::getVirtualFunctionReturnTypeName(const AbstractMetaFunction* func) +{ + if (!func->type()) + return "\"\""; + + if (!func->typeReplaced(0).isEmpty()) + return '"' + func->typeReplaced(0) + '"'; + + // SbkType would return null when the type is a container. + if (func->type()->typeEntry()->isContainer()) + return '"' + reinterpret_cast(func->type()->typeEntry())->typeName() + '"'; + + if (avoidProtectedHack()) { + const AbstractMetaEnum* metaEnum = findAbstractMetaEnum(func->type()); + if (metaEnum && metaEnum->isProtected()) + return '"' + protectedEnumSurrogateName(metaEnum) + '"'; + } + + if (func->type()->isPrimitive()) + return '"' + func->type()->name() + '"'; + + return QString("Shiboken::SbkType< %1 >()->tp_name").arg(func->type()->typeEntry()->qualifiedCppName()); +} + +void CppGenerator::writeVirtualMethodNative(QTextStream&s, const AbstractMetaFunction* func) +{ + //skip metaObject function, this will be written manually ahead + if (usePySideExtensions() && func->ownerClass() && func->ownerClass()->isQObject() && + ((func->name() == "metaObject") || (func->name() == "qt_metacall"))) + return; + + const TypeEntry* retType = func->type() ? func->type()->typeEntry() : 0; + const QString funcName = func->isOperatorOverload() ? pythonOperatorFunctionName(func) : func->name(); + + QString prefix = QString("%1::").arg(wrapperName(func->ownerClass())); + s << functionSignature(func, prefix, "", Generator::SkipDefaultValues|Generator::OriginalTypeDescription) << endl; + s << '{' << endl; + + Indentation indentation(INDENT); + + QString defaultReturnExpr; + if (retType) { + foreach (FunctionModification mod, func->modifications()) { + foreach (ArgumentModification argMod, mod.argument_mods) { + if (argMod.index == 0 && !argMod.replacedDefaultExpression.isEmpty()) { + QRegExp regex("%(\\d+)"); + defaultReturnExpr = argMod.replacedDefaultExpression; + int offset = 0; + while ((offset = regex.indexIn(defaultReturnExpr, offset)) != -1) { + int argId = regex.cap(1).toInt() - 1; + if (argId < 0 || argId > func->arguments().count()) { + ReportHandler::warning("The expression used in return value contains an invalid index."); + break; + } + defaultReturnExpr.replace(regex.cap(0), func->arguments()[argId]->name()); + } + } + } + } + if (defaultReturnExpr.isEmpty()) + defaultReturnExpr = minimalConstructor(func->type()); + if (defaultReturnExpr.isEmpty()) { + QString errorMsg = QString(MIN_CTOR_ERROR_MSG).arg(func->type()->cppSignature()); + ReportHandler::warning(errorMsg); + s << endl << INDENT << "#error " << errorMsg << endl; + } + } + + if (func->isAbstract() && func->isModifiedRemoved()) { + ReportHandler::warning(QString("Pure virtual method '%1::%2' must be implement but was "\ + "completely removed on type system.") + .arg(func->ownerClass()->name()) + .arg(func->minimalSignature())); + s << INDENT << "return " << defaultReturnExpr << ';' << endl; + s << '}' << endl << endl; + return; + } + + //Write declaration/native injected code + if (func->hasInjectedCode()) { + CodeSnipList snips = func->injectedCodeSnips(); + const AbstractMetaArgument* lastArg = func->arguments().isEmpty() ? 0 : func->arguments().last(); + writeCodeSnips(s, snips, CodeSnip::Declaration, TypeSystem::NativeCode, func, lastArg); + s << endl; + } + + s << INDENT << "Shiboken::GilState gil;" << endl; + + // Get out of virtual method call if someone already threw an error. + s << INDENT << "if (PyErr_Occurred())" << endl; + { + Indentation indentation(INDENT); + s << INDENT << "return " << defaultReturnExpr << ';' << endl; + } + + s << INDENT << "Shiboken::AutoDecRef " PYTHON_OVERRIDE_VAR "(Shiboken::BindingManager::instance().getOverride(this, \""; + s << funcName << "\"));" << endl; + + s << INDENT << "if (" PYTHON_OVERRIDE_VAR ".isNull()) {" << endl; + { + Indentation indentation(INDENT); + CodeSnipList snips; + if (func->hasInjectedCode()) { + snips = func->injectedCodeSnips(); + const AbstractMetaArgument* lastArg = func->arguments().isEmpty() ? 0 : func->arguments().last(); + writeCodeSnips(s, snips, CodeSnip::Beginning, TypeSystem::ShellCode, func, lastArg); + s << endl; + } + + if (func->isAbstract()) { + s << INDENT << "PyErr_SetString(PyExc_NotImplementedError, \"pure virtual method '"; + s << func->ownerClass()->name() << '.' << funcName; + s << "()' not implemented.\");" << endl; + s << INDENT << "return " << (retType ? defaultReturnExpr : ""); + } else { + s << INDENT << "gil.release();" << endl; + s << INDENT << "return this->::" << func->implementingClass()->qualifiedCppName() << "::"; + writeFunctionCall(s, func, Generator::VirtualCall); + } + } + s << ';' << endl; + s << INDENT << '}' << endl << endl; + + writeConversionRule(s, func, TypeSystem::TargetLangCode); + + s << INDENT << "Shiboken::AutoDecRef " PYTHON_ARGS "("; + + if (func->arguments().isEmpty() || allArgumentsRemoved(func)) { + s << "PyTuple_New(0));" << endl; + } else { + QStringList argConversions; + foreach (const AbstractMetaArgument* arg, func->arguments()) { + if (func->argumentRemoved(arg->argumentIndex() + 1)) + continue; + + QString argConv; + QTextStream ac(&argConv); + const PrimitiveTypeEntry* argType = (const PrimitiveTypeEntry*) arg->type()->typeEntry(); + bool convert = argType->isObject() + || arg->type()->isQObject() + || argType->isValue() + || arg->type()->isValuePointer() + || arg->type()->isNativePointer() + || argType->isFlags() + || argType->isEnum() + || argType->isContainer() + || arg->type()->isReference(); + + if (!convert && argType->isPrimitive()) { + if (argType->basicAliasedTypeEntry()) + argType = argType->basicAliasedTypeEntry(); + convert = !m_formatUnits.contains(argType->name()); + } + + Indentation indentation(INDENT); + ac << INDENT; + if (!func->conversionRule(TypeSystem::TargetLangCode, arg->argumentIndex() + 1).isEmpty()) { + // Has conversion rule. + ac << QString("%1"CONV_RULE_OUT_VAR_SUFFIX).arg(arg->name()); + } else { + QString argName = arg->name(); + if (convert) + writeToPythonConversion(ac, arg->type(), func->ownerClass(), argName); + else + ac << argName; + } + + argConversions << argConv; + } + + s << "Py_BuildValue(\"(" << getFormatUnitString(func, false) << ")\"," << endl; + s << argConversions.join(",\n") << endl; + s << INDENT << "));" << endl; + } + + bool invalidateReturn = false; + foreach (FunctionModification funcMod, func->modifications()) { + foreach (ArgumentModification argMod, funcMod.argument_mods) { + if (argMod.resetAfterUse) { + s << INDENT << "bool invalidateArg" << argMod.index; + s << " = PyTuple_GET_ITEM(" PYTHON_ARGS ", " << argMod.index - 1 << ")->ob_refcnt == 1;" << endl; + } else if (argMod.index == 0 && argMod.ownerships[TypeSystem::TargetLangCode] == TypeSystem::CppOwnership) { + invalidateReturn = true; + } + } + } + s << endl; + + CodeSnipList snips; + if (func->hasInjectedCode()) { + snips = func->injectedCodeSnips(); + + if (injectedCodeUsesPySelf(func)) + s << INDENT << "PyObject* pySelf = BindingManager::instance().retrieveWrapper(this);" << endl; + + const AbstractMetaArgument* lastArg = func->arguments().isEmpty() ? 0 : func->arguments().last(); + writeCodeSnips(s, snips, CodeSnip::Beginning, TypeSystem::NativeCode, func, lastArg); + s << endl; + } + + if (!injectedCodeCallsPythonOverride(func)) { + s << INDENT; + s << "Shiboken::AutoDecRef " PYTHON_RETURN_VAR "(PyObject_Call(" PYTHON_OVERRIDE_VAR ", " PYTHON_ARGS ", NULL));" << endl; + + s << INDENT << "// An error happened in python code!" << endl; + s << INDENT << "if (" PYTHON_RETURN_VAR ".isNull()) {" << endl; + { + Indentation indent(INDENT); + s << INDENT << "PyErr_Print();" << endl; + s << INDENT << "return " << defaultReturnExpr << ';' << endl; + } + s << INDENT << '}' << endl; + + if (retType) { + if (invalidateReturn) + s << INDENT << "bool invalidateArg0 = " PYTHON_RETURN_VAR "->ob_refcnt == 1;" << endl; + + if (func->typeReplaced(0) != "PyObject") { + + s << INDENT << "// Check return type" << endl; + s << INDENT; + if (func->typeReplaced(0).isEmpty()) { + s << "PythonToCppFunc " PYTHON_TO_CPP_VAR " = " << cpythonIsConvertibleFunction(func->type()); + s << PYTHON_RETURN_VAR ");" << endl; + s << INDENT << "if (!" PYTHON_TO_CPP_VAR ") {" << endl; + { + Indentation indent(INDENT); + s << INDENT << "Shiboken::warning(PyExc_RuntimeWarning, 2, "\ + "\"Invalid return value in function %s, expected %s, got %s.\", \""; + s << func->ownerClass()->name() << '.' << funcName << "\", " << getVirtualFunctionReturnTypeName(func); + s << ", " PYTHON_RETURN_VAR "->ob_type->tp_name);" << endl; + s << INDENT << "return " << defaultReturnExpr << ';' << endl; + } + s << INDENT << '}' << endl; + + } else { + + s << INDENT << "// Check return type" << endl; + s << INDENT << "bool typeIsValid = "; + writeTypeCheck(s, func->type(), PYTHON_RETURN_VAR, + isNumber(func->type()->typeEntry()), func->typeReplaced(0)); + s << ';' << endl; + s << INDENT << "if (!typeIsValid"; + s << (isPointerToWrapperType(func->type()) ? " && " PYTHON_RETURN_VAR " != Py_None" : ""); + s << ") {" << endl; + { + Indentation indent(INDENT); + s << INDENT << "Shiboken::warning(PyExc_RuntimeWarning, 2, "\ + "\"Invalid return value in function %s, expected %s, got %s.\", \""; + s << func->ownerClass()->name() << '.' << funcName << "\", " << getVirtualFunctionReturnTypeName(func); + s << ", " PYTHON_RETURN_VAR "->ob_type->tp_name);" << endl; + s << INDENT << "return " << defaultReturnExpr << ';' << endl; + } + s << INDENT << '}' << endl; + + } + } + + if (!func->conversionRule(TypeSystem::NativeCode, 0).isEmpty()) { + // Has conversion rule. + writeConversionRule(s, func, TypeSystem::NativeCode, CPP_RETURN_VAR); + } else if (!injectedCodeHasReturnValueAttribution(func, TypeSystem::NativeCode)) { + writePythonToCppTypeConversion(s, func->type(), PYTHON_RETURN_VAR, CPP_RETURN_VAR, func->implementingClass()); + } + } + } + + if (invalidateReturn) { + s << INDENT << "if (invalidateArg0)" << endl; + Indentation indentation(INDENT); + s << INDENT << "Shiboken::Object::releaseOwnership(" << PYTHON_RETURN_VAR ".object());" << endl; + } + + foreach (FunctionModification funcMod, func->modifications()) { + foreach (ArgumentModification argMod, funcMod.argument_mods) { + if (argMod.resetAfterUse) { + s << INDENT << "if (invalidateArg" << argMod.index << ')' << endl; + Indentation indentation(INDENT); + s << INDENT << "Shiboken::Object::invalidate(PyTuple_GET_ITEM(" PYTHON_ARGS ", "; + s << (argMod.index - 1) << "));" << endl; + } else if (argMod.ownerships.contains(TypeSystem::NativeCode) + && argMod.index == 0 && argMod.ownerships[TypeSystem::NativeCode] == TypeSystem::CppOwnership) { + s << INDENT << "if (Shiboken::Object::checkType(" PYTHON_RETURN_VAR "))" << endl; + Indentation indent(INDENT); + s << INDENT << "Shiboken::Object::releaseOwnership(" PYTHON_RETURN_VAR ");" << endl; + } + } + } + + if (func->hasInjectedCode()) { + s << endl; + const AbstractMetaArgument* lastArg = func->arguments().isEmpty() ? 0 : func->arguments().last(); + writeCodeSnips(s, snips, CodeSnip::End, TypeSystem::NativeCode, func, lastArg); + } + + if (retType) { + s << INDENT << "return "; + if (avoidProtectedHack() && retType->isEnum()) { + const AbstractMetaEnum* metaEnum = findAbstractMetaEnum(retType); + bool isProtectedEnum = metaEnum && metaEnum->isProtected(); + if (isProtectedEnum) { + QString typeCast; + if (metaEnum->enclosingClass()) + typeCast += QString("::%1").arg(metaEnum->enclosingClass()->qualifiedCppName()); + typeCast += QString("::%1").arg(metaEnum->name()); + s << '(' << typeCast << ')'; + } + } + if (func->type()->isReference() && !isPointer(func->type())) + s << '*'; + s << CPP_RETURN_VAR ";" << endl; + } + + s << '}' << endl << endl; +} + +void CppGenerator::writeMetaObjectMethod(QTextStream& s, const AbstractMetaClass* metaClass) +{ + Indentation indentation(INDENT); + QString wrapperClassName = wrapperName(metaClass); + s << "const QMetaObject* " << wrapperClassName << "::metaObject() const" << endl; + s << '{' << endl; + s << INDENT << "#if QT_VERSION >= 0x040700" << endl; + s << INDENT << "if (QObject::d_ptr->metaObject) return QObject::d_ptr->metaObject;" << endl; + s << INDENT << "#endif" << endl; + s << INDENT << "SbkObject* pySelf = Shiboken::BindingManager::instance().retrieveWrapper(this);" << endl; + s << INDENT << "return PySide::SignalManager::retriveMetaObject(reinterpret_cast(pySelf));" << endl; + s << '}' << endl << endl; + + // qt_metacall function + s << "int " << wrapperClassName << "::qt_metacall(QMetaObject::Call call, int id, void** args)" << endl; + s << "{" << endl; + + AbstractMetaFunction *func = NULL; + AbstractMetaFunctionList list = metaClass->queryFunctionsByName("qt_metacall"); + if (list.size() == 1) + func = list[0]; + + CodeSnipList snips; + if (func) { + snips = func->injectedCodeSnips(); + if (func->isUserAdded()) { + CodeSnipList snips = func->injectedCodeSnips(); + writeCodeSnips(s, snips, CodeSnip::Any, TypeSystem::NativeCode, func); + } + } + + s << INDENT << "int result = " << metaClass->qualifiedCppName() << "::qt_metacall(call, id, args);" << endl; + s << INDENT << "return result < 0 ? result : PySide::SignalManager::qt_metacall(this, call, id, args);" << endl; + s << "}" << endl << endl; + + // qt_metacast function + writeMetaCast(s, metaClass); +} + +void CppGenerator::writeMetaCast(QTextStream& s, const AbstractMetaClass* metaClass) +{ + Indentation indentation(INDENT); + QString wrapperClassName = wrapperName(metaClass); + s << "void* " << wrapperClassName << "::qt_metacast(const char* _clname)" << endl; + s << '{' << endl; + s << INDENT << "if (!_clname) return 0;" << endl; + s << INDENT << "SbkObject* pySelf = Shiboken::BindingManager::instance().retrieveWrapper(this);" << endl; + s << INDENT << "if (pySelf && PySide::inherits(Py_TYPE(pySelf), _clname))" << endl; + s << INDENT << INDENT << "return static_cast(const_cast< " << wrapperClassName << "* >(this));" << endl; + s << INDENT << "return " << metaClass->qualifiedCppName() << "::qt_metacast(_clname);" << endl; + s << "}" << endl << endl; +} + +void CppGenerator::writeEnumConverterFunctions(QTextStream& s, const AbstractMetaEnum* metaEnum) +{ + if (metaEnum->isPrivate() || metaEnum->isAnonymous()) + return; + writeEnumConverterFunctions(s, metaEnum->typeEntry()); +} + +void CppGenerator::writeEnumConverterFunctions(QTextStream& s, const TypeEntry* enumType) +{ + if (!enumType) + return; + QString enumFlagName = enumType->isFlags() ? "flag" : "enum"; + QString typeName = fixedCppTypeName(enumType); + QString enumPythonType = cpythonTypeNameExt(enumType); + QString cppTypeName = getFullTypeName(enumType).trimmed(); + if (avoidProtectedHack()) { + const AbstractMetaEnum* metaEnum = findAbstractMetaEnum(enumType); + if (metaEnum && metaEnum->isProtected()) + cppTypeName = protectedEnumSurrogateName(metaEnum); + } + QString code; + QTextStream c(&code); + c << INDENT << "*((" << cppTypeName << "*)cppOut) = "; + if (enumType->isFlags()) + c << cppTypeName << "(QFlag(PySide::QFlags::getValue(reinterpret_cast(pyIn))))"; + else + c << "(" << cppTypeName << ") Shiboken::Enum::getValue(pyIn)"; + c << ';' << endl; + writePythonToCppFunction(s, code, typeName, typeName); + + QString pyTypeCheck = QString("PyObject_TypeCheck(pyIn, %1)").arg(enumPythonType); + writeIsPythonConvertibleToCppFunction(s, typeName, typeName, pyTypeCheck); + + code.clear(); + + c << INDENT << "int castCppIn = *((" << cppTypeName << "*)cppIn);" << endl; + c << INDENT; + c << "return "; + if (enumType->isFlags()) + c << "reinterpret_cast(PySide::QFlags::newObject(castCppIn, " << enumPythonType << "))"; + + else + c << "Shiboken::Enum::newItem(" << enumPythonType << ", castCppIn)"; + c << ';' << endl; + writeCppToPythonFunction(s, code, typeName, typeName); + s << endl; + + if (enumType->isFlags()) + return; + + const FlagsTypeEntry* flags = reinterpret_cast(enumType)->flags(); + if (!flags) + return; + + // QFlags part. + + writeEnumConverterFunctions(s, flags); + + code.clear(); + cppTypeName = getFullTypeName(flags).trimmed(); + c << INDENT << "*((" << cppTypeName << "*)cppOut) = " << cppTypeName; + c << "(QFlag(Shiboken::Enum::getValue(pyIn)));" << endl; + + QString flagsTypeName = fixedCppTypeName(flags); + writePythonToCppFunction(s, code, typeName, flagsTypeName); + writeIsPythonConvertibleToCppFunction(s, typeName, flagsTypeName, pyTypeCheck); + + code.clear(); + c << INDENT << "Shiboken::AutoDecRef pyLong(PyNumber_Long(pyIn));" << endl; + c << INDENT << "*((" << cppTypeName << "*)cppOut) = " << cppTypeName; + c << "(QFlag(PyLong_AsLong(pyLong.object())));" << endl; + writePythonToCppFunction(s, code, "number", flagsTypeName); + writeIsPythonConvertibleToCppFunction(s, "number", flagsTypeName, "PyNumber_Check(pyIn)"); +} + +void CppGenerator::writeConverterFunctions(QTextStream& s, const AbstractMetaClass* metaClass) +{ + s << "// Type conversion functions." << endl << endl; + + AbstractMetaEnumList classEnums = metaClass->enums(); + foreach (AbstractMetaClass* innerClass, metaClass->innerClasses()) + lookForEnumsInClassesNotToBeGenerated(classEnums, innerClass); + if (!classEnums.isEmpty()) + s << "// Python to C++ enum conversion." << endl; + foreach (const AbstractMetaEnum* metaEnum, classEnums) + writeEnumConverterFunctions(s, metaEnum); + + if (metaClass->isNamespace()) + return; + + QString typeName = getFullTypeName(metaClass); + QString cpythonType = cpythonTypeName(metaClass); + + // Returns the C++ pointer of the Python wrapper. + s << "// Python to C++ pointer conversion - returns the C++ object of the Python wrapper (keeps object identity)." << endl; + + QString sourceTypeName = metaClass->name(); + QString targetTypeName = QString("%1_PTR").arg(metaClass->name()); + QString code; + QTextStream c(&code); + c << INDENT << "Shiboken::Conversions::pythonToCppPointer(&" << cpythonType << ", pyIn, cppOut);"; + writePythonToCppFunction(s, code, sourceTypeName, targetTypeName); + + // "Is convertible" function for the Python object to C++ pointer conversion. + QString pyTypeCheck = QString("PyObject_TypeCheck(pyIn, (PyTypeObject*)&%1)").arg(cpythonType); + writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, pyTypeCheck, QString(), true); + s << endl; + + // C++ pointer to a Python wrapper, keeping identity. + s << "// C++ to Python pointer conversion - tries to find the Python wrapper for the C++ object (keeps object identity)." << endl; + code.clear(); + c << INDENT << "PyObject* pyOut = (PyObject*)Shiboken::BindingManager::instance().retrieveWrapper(cppIn);" << endl; + c << INDENT << "if (pyOut) {" << endl; + { + Indentation indent(INDENT); + c << INDENT << "Py_INCREF(pyOut);" << endl; + c << INDENT << "return pyOut;" << endl; + } + c << INDENT << '}' << endl; + c << INDENT << "const char* typeName = typeid(*((" << typeName << "*)cppIn)).name();" << endl; + c << INDENT << "return Shiboken::Object::newObject(&" << cpythonType; + c << ", const_cast(cppIn), false, false, typeName);"; + std::swap(targetTypeName, sourceTypeName); + writeCppToPythonFunction(s, code, sourceTypeName, targetTypeName); + + // The conversions for an Object Type end here. + if (!metaClass->typeEntry()->isValue()) { + s << endl; + return; + } + + // Always copies C++ value (not pointer, and not reference) to a new Python wrapper. + s << endl << "// C++ to Python copy conversion." << endl; + sourceTypeName = QString("%1_COPY").arg(metaClass->name()); + targetTypeName = metaClass->name(); + code.clear(); + c << INDENT << "return Shiboken::Object::newObject(&" << cpythonType << ", new ::" << wrapperName(metaClass); + c << "(*((" << typeName << "*)cppIn)), true, true);"; + writeCppToPythonFunction(s, code, sourceTypeName, targetTypeName); + s << endl; + + // Python to C++ copy conversion. + s << "// Python to C++ copy conversion." << endl; + sourceTypeName = metaClass->name(); + targetTypeName = QString("%1_COPY").arg(sourceTypeName); + code.clear(); + c << INDENT << "*((" << typeName << "*)cppOut) = *" << cpythonWrapperCPtr(metaClass->typeEntry(), "pyIn") << ';'; + writePythonToCppFunction(s, code, sourceTypeName, targetTypeName); + + // "Is convertible" function for the Python object to C++ value copy conversion. + writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, pyTypeCheck); + s << endl; + + // User provided implicit conversions. + CustomConversion* customConversion = metaClass->typeEntry()->customConversion(); + + // Implicit conversions. + AbstractMetaFunctionList implicitConvs; + if (!customConversion || !customConversion->replaceOriginalTargetToNativeConversions()) { + foreach (AbstractMetaFunction* func, implicitConversions(metaClass->typeEntry())) { + if (!func->isUserAdded()) + implicitConvs << func; + } + } + + if (!implicitConvs.isEmpty()) + s << "// Implicit conversions." << endl; + + AbstractMetaType* targetType = buildAbstractMetaTypeFromAbstractMetaClass(metaClass); + foreach (const AbstractMetaFunction* conv, implicitConvs) { + if (conv->isModifiedRemoved()) + continue; + + QString typeCheck; + QString toCppConv; + QString toCppPreConv; + if (conv->isConversionOperator()) { + const AbstractMetaClass* sourceClass = conv->ownerClass(); + typeCheck = QString("PyObject_TypeCheck(pyIn, %1)").arg(cpythonTypeNameExt(sourceClass->typeEntry())); + toCppConv = QString("*%1").arg(cpythonWrapperCPtr(sourceClass->typeEntry(), "pyIn")); + + } else { + // Constructor that does implicit conversion. + if (!conv->typeReplaced(1).isEmpty()) + continue; + const AbstractMetaType* sourceType = conv->arguments().first()->type(); + typeCheck = cpythonCheckFunction(sourceType); + bool isUserPrimitiveWithoutTargetLangName = isUserPrimitive(sourceType) + && sourceType->typeEntry()->targetLangApiName() == sourceType->typeEntry()->name(); + if (!isWrapperType(sourceType) + && !isUserPrimitiveWithoutTargetLangName + && !sourceType->typeEntry()->isEnum() + && !sourceType->typeEntry()->isFlags() + && !sourceType->typeEntry()->isContainer()) { + typeCheck += '('; + } + if (isWrapperType(sourceType)) { + typeCheck = QString("%1pyIn)").arg(typeCheck); + toCppConv = QString("%1%2") + .arg((sourceType->isReference() || !isPointerToWrapperType(sourceType)) ? "*" : "") + .arg(cpythonWrapperCPtr(sourceType->typeEntry(), "pyIn")); + } else if (typeCheck.contains("%in")) { + typeCheck.replace("%in", "pyIn"); + typeCheck = QString("%1)").arg(typeCheck); + } else { + typeCheck = QString("%1pyIn)").arg(typeCheck); + } + + if (isUserPrimitive(sourceType) + || isCppPrimitive(sourceType) + || sourceType->typeEntry()->isContainer() + || sourceType->typeEntry()->isEnum() + || sourceType->typeEntry()->isFlags()) { + QTextStream pc(&toCppPreConv); + pc << INDENT << getFullTypeNameWithoutModifiers(sourceType) << " cppIn"; + writeMinimalConstructorExpression(pc, sourceType); + pc << ';' << endl; + writeToCppConversion(pc, sourceType, 0, "pyIn", "cppIn"); + pc << ';'; + toCppConv.append("cppIn"); + } else if (!isWrapperType(sourceType)) { + QTextStream tcc(&toCppConv); + writeToCppConversion(tcc, sourceType, metaClass, "pyIn", "/*BOZO-1061*/"); + } + + + } + const AbstractMetaType* sourceType = conv->isConversionOperator() + ? buildAbstractMetaTypeFromAbstractMetaClass(conv->ownerClass()) + : conv->arguments().first()->type(); + writePythonToCppConversionFunctions(s, sourceType, targetType, typeCheck, toCppConv, toCppPreConv); + } + + writeCustomConverterFunctions(s, customConversion); +} + +void CppGenerator::writeCustomConverterFunctions(QTextStream& s, const CustomConversion* customConversion) +{ + if (!customConversion) + return; + const CustomConversion::TargetToNativeConversions& toCppConversions = customConversion->targetToNativeConversions(); + if (toCppConversions.isEmpty()) + return; + s << "// Python to C++ conversions for type '" << customConversion->ownerType()->qualifiedCppName() << "'." << endl; + foreach (CustomConversion::TargetToNativeConversion* toNative, toCppConversions) + writePythonToCppConversionFunctions(s, toNative, customConversion->ownerType()); + s << endl; +} + +void CppGenerator::writeConverterRegister(QTextStream& s, const AbstractMetaClass* metaClass) +{ + if (metaClass->isNamespace()) + return; + s << INDENT << "// Register Converter" << endl; + s << INDENT << "SbkConverter* converter = Shiboken::Conversions::createConverter(&"; + s << cpythonTypeName(metaClass) << ',' << endl; + { + Indentation indent(INDENT); + QString sourceTypeName = metaClass->name(); + QString targetTypeName = QString("%1_PTR").arg(metaClass->name()); + s << INDENT << pythonToCppFunctionName(sourceTypeName, targetTypeName) << ',' << endl; + s << INDENT << convertibleToCppFunctionName(sourceTypeName, targetTypeName) << ',' << endl; + std::swap(targetTypeName, sourceTypeName); + s << INDENT << cppToPythonFunctionName(sourceTypeName, targetTypeName); + if (metaClass->typeEntry()->isValue()) { + s << ',' << endl; + sourceTypeName = QString("%1_COPY").arg(metaClass->name()); + s << INDENT << cppToPythonFunctionName(sourceTypeName, targetTypeName); + } + } + s << ");" << endl; + + s << endl; + + QStringList cppSignature = metaClass->qualifiedCppName().split("::", QString::SkipEmptyParts); + while (!cppSignature.isEmpty()) { + QString signature = cppSignature.join("::"); + s << INDENT << "Shiboken::Conversions::registerConverterName(converter, \"" << signature << "\");" << endl; + s << INDENT << "Shiboken::Conversions::registerConverterName(converter, \"" << signature << "*\");" << endl; + s << INDENT << "Shiboken::Conversions::registerConverterName(converter, \"" << signature << "&\");" << endl; + cppSignature.removeFirst(); + } + + s << INDENT << "Shiboken::Conversions::registerConverterName(converter, typeid(::"; + s << metaClass->qualifiedCppName() << ").name());" << endl; + if (shouldGenerateCppWrapper(metaClass)) { + s << INDENT << "Shiboken::Conversions::registerConverterName(converter, typeid(::"; + s << wrapperName(metaClass) << ").name());" << endl; + } + + s << endl; + + if (!metaClass->typeEntry()->isValue()) + return; + + // Python to C++ copy (value, not pointer neither reference) conversion. + s << INDENT << "// Add Python to C++ copy (value, not pointer neither reference) conversion to type converter." << endl; + QString sourceTypeName = metaClass->name(); + QString targetTypeName = QString("%1_COPY").arg(metaClass->name()); + QString toCpp = pythonToCppFunctionName(sourceTypeName, targetTypeName); + QString isConv = convertibleToCppFunctionName(sourceTypeName, targetTypeName); + writeAddPythonToCppConversion(s, "converter", toCpp, isConv); + + // User provided implicit conversions. + CustomConversion* customConversion = metaClass->typeEntry()->customConversion(); + + // Add implicit conversions. + AbstractMetaFunctionList implicitConvs; + if (!customConversion || !customConversion->replaceOriginalTargetToNativeConversions()) { + foreach (AbstractMetaFunction* func, implicitConversions(metaClass->typeEntry())) { + if (!func->isUserAdded()) + implicitConvs << func; + } + } + + if (!implicitConvs.isEmpty()) + s << INDENT << "// Add implicit conversions to type converter." << endl; + + AbstractMetaType* targetType = buildAbstractMetaTypeFromAbstractMetaClass(metaClass); + foreach (const AbstractMetaFunction* conv, implicitConvs) { + if (conv->isModifiedRemoved()) + continue; + const AbstractMetaType* sourceType; + if (conv->isConversionOperator()) { + sourceType = buildAbstractMetaTypeFromAbstractMetaClass(conv->ownerClass()); + } else { + // Constructor that does implicit conversion. + if (!conv->typeReplaced(1).isEmpty()) + continue; + sourceType = conv->arguments().first()->type(); + } + QString toCpp = pythonToCppFunctionName(sourceType, targetType); + QString isConv = convertibleToCppFunctionName(sourceType, targetType); + writeAddPythonToCppConversion(s, "converter", toCpp, isConv); + } + + writeCustomConverterRegister(s, customConversion, "converter"); +} + +void CppGenerator::writeCustomConverterRegister(QTextStream& s, const CustomConversion* customConversion, const QString& converterVar) +{ + if (!customConversion) + return; + const CustomConversion::TargetToNativeConversions& toCppConversions = customConversion->targetToNativeConversions(); + if (toCppConversions.isEmpty()) + return; + s << INDENT << "// Add user defined implicit conversions to type converter." << endl; + foreach (CustomConversion::TargetToNativeConversion* toNative, toCppConversions) { + QString toCpp = pythonToCppFunctionName(toNative, customConversion->ownerType()); + QString isConv = convertibleToCppFunctionName(toNative, customConversion->ownerType()); + writeAddPythonToCppConversion(s, converterVar, toCpp, isConv); + } +} + +void CppGenerator::writeContainerConverterRegister(QTextStream& s, const AbstractMetaType* container, const QString& converterVar) +{ + s << INDENT << "// Add user defined container conversion to type converter." << endl; + QString typeName = fixedCppTypeName(container); + QString toCpp = pythonToCppFunctionName(typeName, typeName); + QString isConv = convertibleToCppFunctionName(typeName, typeName); + writeAddPythonToCppConversion(s, converterVar, toCpp, isConv); +} + +void CppGenerator::writeContainerConverterFunctions(QTextStream& s, const AbstractMetaType* containerType) +{ + writeCppToPythonFunction(s, containerType); + writePythonToCppConversionFunctions(s, containerType); +} + +void CppGenerator::writeMethodWrapperPreamble(QTextStream& s, OverloadData& overloadData) +{ + const AbstractMetaFunction* rfunc = overloadData.referenceFunction(); + const AbstractMetaClass* ownerClass = rfunc->ownerClass(); + int minArgs = overloadData.minArgs(); + int maxArgs = overloadData.maxArgs(); + bool initPythonArguments; + bool usesNamedArguments; + + // If method is a constructor... + if (rfunc->isConstructor()) { + // Check if the right constructor was called. + if (!ownerClass->hasPrivateDestructor()) { + s << INDENT; + s << "if (Shiboken::Object::isUserType(" PYTHON_SELF_VAR ") && !Shiboken::ObjectType::canCallConstructor(" PYTHON_SELF_VAR "->ob_type, Shiboken::SbkType< ::"; + s << ownerClass->qualifiedCppName() << " >()))" << endl; + Indentation indent(INDENT); + s << INDENT << "return " << m_currentErrorCode << ';' << endl << endl; + } + // Declare pointer for the underlying C++ object. + s << INDENT << "::"; + s << (shouldGenerateCppWrapper(ownerClass) ? wrapperName(ownerClass) : ownerClass->qualifiedCppName()); + s << "* cptr = 0;" << endl; + + initPythonArguments = maxArgs > 0; + usesNamedArguments = !ownerClass->isQObject() && overloadData.hasArgumentWithDefaultValue(); + + } else { + if (rfunc->implementingClass() && + (!rfunc->implementingClass()->isNamespace() && overloadData.hasInstanceFunction())) { + writeCppSelfDefinition(s, rfunc, overloadData.hasStaticFunction()); + } + if (!rfunc->isInplaceOperator() && overloadData.hasNonVoidReturnType()) + s << INDENT << "PyObject* " PYTHON_RETURN_VAR " = 0;" << endl; + + initPythonArguments = minArgs != maxArgs || maxArgs > 1; + usesNamedArguments = rfunc->isCallOperator() || overloadData.hasArgumentWithDefaultValue(); + } + + if (maxArgs > 0) { + s << INDENT << "int overloadId = -1;" << endl; + s << INDENT << "PythonToCppFunc " PYTHON_TO_CPP_VAR; + if (pythonFunctionWrapperUsesListOfArguments(overloadData)) + s << "[] = { 0" << QString(", 0").repeated(maxArgs-1) << " }"; + s << ';' << endl; + writeUnusedVariableCast(s, PYTHON_TO_CPP_VAR); + } + + if (usesNamedArguments && !rfunc->isCallOperator()) + s << INDENT << "int numNamedArgs = (kwds ? PyDict_Size(kwds) : 0);" << endl; + + if (initPythonArguments) { + s << INDENT << "int numArgs = "; + if (minArgs == 0 && maxArgs == 1 && !rfunc->isConstructor() && !pythonFunctionWrapperUsesListOfArguments(overloadData)) + s << "(" PYTHON_ARG " == 0 ? 0 : 1);" << endl; + else + writeArgumentsInitializer(s, overloadData); + } +} + +void CppGenerator::writeConstructorWrapper(QTextStream& s, const AbstractMetaFunctionList overloads) +{ + ErrorCode errorCode(-1); + OverloadData overloadData(overloads, this); + + const AbstractMetaFunction* rfunc = overloadData.referenceFunction(); + const AbstractMetaClass* metaClass = rfunc->ownerClass(); + + s << "static int" << endl; + s << cpythonFunctionName(rfunc) << "(PyObject* " PYTHON_SELF_VAR ", PyObject* args, PyObject* kwds)" << endl; + s << '{' << endl; + + QSet argNamesSet; + if (usePySideExtensions() && metaClass->isQObject()) { + // Write argNames variable with all known argument names. + foreach (const AbstractMetaFunction* func, overloadData.overloads()) { + foreach (const AbstractMetaArgument* arg, func->arguments()) { + if (arg->defaultValueExpression().isEmpty() || func->argumentRemoved(arg->argumentIndex() + 1)) + continue; + argNamesSet << arg->name(); + } + } + QStringList argNamesList = argNamesSet.toList(); + qSort(argNamesList.begin(), argNamesList.end()); + if (argNamesList.isEmpty()) + s << INDENT << "const char** argNames = 0;" << endl; + else + s << INDENT << "const char* argNames[] = {\"" << argNamesList.join("\", \"") << "\"};" << endl; + s << INDENT << "const QMetaObject* metaObject;" << endl; + } + + s << INDENT << "SbkObject* sbkSelf = reinterpret_cast(" PYTHON_SELF_VAR ");" << endl; + + if (metaClass->isAbstract() || metaClass->baseClassNames().size() > 1) { + s << INDENT << "SbkObjectType* type = reinterpret_cast(" PYTHON_SELF_VAR "->ob_type);" << endl; + s << INDENT << "SbkObjectType* myType = reinterpret_cast(" << cpythonTypeNameExt(metaClass->typeEntry()) << ");" << endl; + } + + if (metaClass->isAbstract()) { + s << INDENT << "if (type == myType) {" << endl; + { + Indentation indent(INDENT); + s << INDENT << "PyErr_SetString(PyExc_NotImplementedError," << endl; + { + Indentation indentation(INDENT); + s << INDENT << "\"'" << metaClass->qualifiedCppName(); + } + s << "' represents a C++ abstract class and cannot be instantiated\");" << endl; + s << INDENT << "return " << m_currentErrorCode << ';' << endl; + } + s << INDENT << '}' << endl << endl; + } + + if (metaClass->baseClassNames().size() > 1) { + if (!metaClass->isAbstract()) { + s << INDENT << "if (type != myType) {" << endl; + } + { + Indentation indentation(INDENT); + s << INDENT << "Shiboken::ObjectType::copyMultimpleheritance(type, myType);" << endl; + } + if (!metaClass->isAbstract()) + s << INDENT << '}' << endl << endl; + } + + writeMethodWrapperPreamble(s, overloadData); + + s << endl; + + if (overloadData.maxArgs() > 0) + writeOverloadedFunctionDecisor(s, overloadData); + + writeFunctionCalls(s, overloadData); + s << endl; + + s << INDENT << "if (PyErr_Occurred() || !Shiboken::Object::setCppPointer(sbkSelf, Shiboken::SbkType< ::" << metaClass->qualifiedCppName() << " >(), cptr)) {" << endl; + { + Indentation indent(INDENT); + s << INDENT << "delete cptr;" << endl; + s << INDENT << "return " << m_currentErrorCode << ';' << endl; + } + s << INDENT << '}' << endl; + if (overloadData.maxArgs() > 0) { + s << INDENT << "if (!cptr) goto " << cpythonFunctionName(rfunc) << "_TypeError;" << endl; + s << endl; + } + + s << INDENT << "Shiboken::Object::setValidCpp(sbkSelf, true);" << endl; + // If the created C++ object has a C++ wrapper the ownership is assigned to Python + // (first "1") and the flag indicating that the Python wrapper holds an C++ wrapper + // is marked as true (the second "1"). Otherwise the default values apply: + // Python owns it and C++ wrapper is false. + if (shouldGenerateCppWrapper(overloads.first()->ownerClass())) + s << INDENT << "Shiboken::Object::setHasCppWrapper(sbkSelf, true);" << endl; + s << INDENT << "Shiboken::BindingManager::instance().registerWrapper(sbkSelf, cptr);" << endl; + + // Create metaObject and register signal/slot + if (metaClass->isQObject() && usePySideExtensions()) { + s << endl << INDENT << "// QObject setup" << endl; + s << INDENT << "PySide::Signal::updateSourceObject(" PYTHON_SELF_VAR ");" << endl; + s << INDENT << "metaObject = cptr->metaObject(); // <- init python qt properties" << endl; + s << INDENT << "if (kwds && !PySide::fillQtProperties(" PYTHON_SELF_VAR ", metaObject, kwds, argNames, " << argNamesSet.count() << "))" << endl; + { + Indentation indentation(INDENT); + s << INDENT << "return " << m_currentErrorCode << ';' << endl; + } + } + + // Constructor code injections, position=end + bool hasCodeInjectionsAtEnd = false; + foreach(AbstractMetaFunction* func, overloads) { + foreach (CodeSnip cs, func->injectedCodeSnips()) { + if (cs.position == CodeSnip::End) { + hasCodeInjectionsAtEnd = true; + break; + } + } + } + if (hasCodeInjectionsAtEnd) { + // FIXME: C++ arguments are not available in code injection on constructor when position = end. + s << INDENT << "switch(overloadId) {" << endl; + foreach(AbstractMetaFunction* func, overloads) { + Indentation indent(INDENT); + foreach (CodeSnip cs, func->injectedCodeSnips()) { + if (cs.position == CodeSnip::End) { + s << INDENT << "case " << metaClass->functions().indexOf(func) << ':' << endl; + s << INDENT << '{' << endl; + { + Indentation indent(INDENT); + writeCodeSnips(s, func->injectedCodeSnips(), CodeSnip::End, TypeSystem::TargetLangCode, func); + } + s << INDENT << '}' << endl; + break; + } + } + } + s << '}' << endl; + } + + s << endl; + s << endl << INDENT << "return 1;" << endl; + if (overloadData.maxArgs() > 0) + writeErrorSection(s, overloadData); + s << '}' << endl << endl; +} + +void CppGenerator::writeMethodWrapper(QTextStream& s, const AbstractMetaFunctionList overloads) +{ + OverloadData overloadData(overloads, this); + const AbstractMetaFunction* rfunc = overloadData.referenceFunction(); + + int maxArgs = overloadData.maxArgs(); + + s << "static PyObject* "; + s << cpythonFunctionName(rfunc) << "(PyObject* " PYTHON_SELF_VAR; + if (maxArgs > 0) { + s << ", PyObject* " << (pythonFunctionWrapperUsesListOfArguments(overloadData) ? "args" : PYTHON_ARG); + if (overloadData.hasArgumentWithDefaultValue() || rfunc->isCallOperator()) + s << ", PyObject* kwds"; + } + s << ')' << endl << '{' << endl; + + writeMethodWrapperPreamble(s, overloadData); + + s << endl; + + /* + * Make sure reverse <> operators defined in other classes (specially from other modules) + * are called. A proper and generic solution would require an reengineering in the operator + * system like the extended converters. + * + * Solves #119 - QDataStream <> operators not working for QPixmap + * http://bugs.openbossa.org/show_bug.cgi?id=119 + */ + bool hasReturnValue = overloadData.hasNonVoidReturnType(); + bool callExtendedReverseOperator = hasReturnValue + && !rfunc->isInplaceOperator() + && !rfunc->isCallOperator() + && rfunc->isOperatorOverload(); + if (callExtendedReverseOperator) { + QString revOpName = ShibokenGenerator::pythonOperatorFunctionName(rfunc).insert(2, 'r'); + if (rfunc->isBinaryOperator()) { + s << INDENT << "if (!isReverse" << endl; + { + Indentation indent(INDENT); + s << INDENT << "&& Shiboken::Object::checkType(" PYTHON_ARG ")" << endl; + s << INDENT << "&& !PyObject_TypeCheck(" PYTHON_ARG ", " PYTHON_SELF_VAR "->ob_type)" << endl; + s << INDENT << "&& PyObject_HasAttrString(" PYTHON_ARG ", const_cast(\"" << revOpName << "\"))) {" << endl; + + // This PyObject_CallMethod call will emit lots of warnings like + // "deprecated conversion from string constant to char *" during compilation + // due to the method name argument being declared as "char*" instead of "const char*" + // issue 6952 http://bugs.python.org/issue6952 + s << INDENT << "PyObject* revOpMethod = PyObject_GetAttrString(" PYTHON_ARG ", const_cast(\"" << revOpName << "\"));" << endl; + s << INDENT << "if (revOpMethod && PyCallable_Check(revOpMethod)) {" << endl; + { + Indentation indent(INDENT); + s << INDENT << PYTHON_RETURN_VAR " = PyObject_CallFunction(revOpMethod, const_cast(\"O\"), " PYTHON_SELF_VAR ");" << endl; + s << INDENT << "if (PyErr_Occurred() && (PyErr_ExceptionMatches(PyExc_NotImplementedError)"; + s << " || PyErr_ExceptionMatches(PyExc_AttributeError))) {" << endl; + { + Indentation indent(INDENT); + s << INDENT << "PyErr_Clear();" << endl; + s << INDENT << "Py_XDECREF(" PYTHON_RETURN_VAR ");" << endl; + s << INDENT << PYTHON_RETURN_VAR " = 0;" << endl; + } + s << INDENT << '}' << endl; + } + s << INDENT << "}" << endl; + s << INDENT << "Py_XDECREF(revOpMethod);" << endl << endl; + } + s << INDENT << "}" << endl; + } + s << INDENT << "// Do not enter here if other object has implemented a reverse operator." << endl; + s << INDENT << "if (!" PYTHON_RETURN_VAR ") {" << endl << endl; + } + + if (maxArgs > 0) + writeOverloadedFunctionDecisor(s, overloadData); + + writeFunctionCalls(s, overloadData); + + if (callExtendedReverseOperator) + s << endl << INDENT << "} // End of \"if (!" PYTHON_RETURN_VAR ")\"" << endl; + + s << endl; + + writeFunctionReturnErrorCheckSection(s, hasReturnValue && !rfunc->isInplaceOperator()); + + if (hasReturnValue) { + if (rfunc->isInplaceOperator()) { + s << INDENT << "Py_INCREF(" PYTHON_SELF_VAR ");\n"; + s << INDENT << "return " PYTHON_SELF_VAR ";\n"; + } else { + s << INDENT << "return " PYTHON_RETURN_VAR ";\n"; + } + } else { + s << INDENT << "Py_RETURN_NONE;" << endl; + } + + if (maxArgs > 0) + writeErrorSection(s, overloadData); + + s << '}' << endl << endl; +} + +void CppGenerator::writeArgumentsInitializer(QTextStream& s, OverloadData& overloadData) +{ + const AbstractMetaFunction* rfunc = overloadData.referenceFunction(); + s << "PyTuple_GET_SIZE(args);" << endl; + + int minArgs = overloadData.minArgs(); + int maxArgs = overloadData.maxArgs(); + + s << INDENT << "PyObject* "; + s << PYTHON_ARGS "[] = {" << QString(maxArgs, '0').split("", QString::SkipEmptyParts).join(", ") << "};" << endl; + s << endl; + + if (overloadData.hasVarargs()) { + maxArgs--; + if (minArgs > maxArgs) + minArgs = maxArgs; + + s << INDENT << "PyObject* nonvarargs = PyTuple_GetSlice(args, 0, " << maxArgs << ");" << endl; + s << INDENT << "Shiboken::AutoDecRef auto_nonvarargs(nonvarargs);" << endl; + s << INDENT << PYTHON_ARGS "[" << maxArgs << "] = PyTuple_GetSlice(args, " << maxArgs << ", numArgs);" << endl; + s << INDENT << "Shiboken::AutoDecRef auto_varargs(" PYTHON_ARGS "[" << maxArgs << "]);" << endl; + s << endl; + } + + bool usesNamedArguments = overloadData.hasArgumentWithDefaultValue(); + + s << INDENT << "// invalid argument lengths" << endl; + bool ownerClassIsQObject = rfunc->ownerClass() && rfunc->ownerClass()->isQObject() && rfunc->isConstructor(); + if (usesNamedArguments) { + if (!ownerClassIsQObject) { + s << INDENT << "if (numArgs" << (overloadData.hasArgumentWithDefaultValue() ? " + numNamedArgs" : "") << " > " << maxArgs << ") {" << endl; + { + Indentation indent(INDENT); + s << INDENT << "PyErr_SetString(PyExc_TypeError, \"" << fullPythonFunctionName(rfunc) << "(): too many arguments\");" << endl; + s << INDENT << "return " << m_currentErrorCode << ';' << endl; + } + s << INDENT << '}'; + } + if (minArgs > 0) { + if (ownerClassIsQObject) + s << INDENT; + else + s << " else "; + s << "if (numArgs < " << minArgs << ") {" << endl; + { + Indentation indent(INDENT); + s << INDENT << "PyErr_SetString(PyExc_TypeError, \"" << fullPythonFunctionName(rfunc) << "(): not enough arguments\");" << endl; + s << INDENT << "return " << m_currentErrorCode << ';' << endl; + } + s << INDENT << '}'; + } + } + QList invalidArgsLength = overloadData.invalidArgumentLengths(); + if (!invalidArgsLength.isEmpty()) { + QStringList invArgsLen; + foreach (int i, invalidArgsLength) + invArgsLen << QString("numArgs == %1").arg(i); + if (usesNamedArguments && (!ownerClassIsQObject || minArgs > 0)) + s << " else "; + else + s << INDENT; + s << "if (" << invArgsLen.join(" || ") << ")" << endl; + Indentation indent(INDENT); + s << INDENT << "goto " << cpythonFunctionName(rfunc) << "_TypeError;"; + } + s << endl << endl; + + QString funcName; + if (rfunc->isOperatorOverload()) + funcName = ShibokenGenerator::pythonOperatorFunctionName(rfunc); + else + funcName = rfunc->name(); + + QString argsVar = overloadData.hasVarargs() ? "nonvarargs" : "args"; + s << INDENT << "if (!"; + if (usesNamedArguments) + s << "PyArg_ParseTuple(" << argsVar << ", \"|" << QByteArray(maxArgs, 'O') << ':' << funcName << '"'; + else + s << "PyArg_UnpackTuple(" << argsVar << ", \"" << funcName << "\", " << minArgs << ", " << maxArgs; + QStringList palist; + for (int i = 0; i < maxArgs; i++) + palist << QString("&(" PYTHON_ARGS "[%1])").arg(i); + s << ", " << palist.join(", ") << "))" << endl; + { + Indentation indent(INDENT); + s << INDENT << "return " << m_currentErrorCode << ';' << endl; + } + s << endl; +} + +void CppGenerator::writeCppSelfDefinition(QTextStream& s, const AbstractMetaClass* metaClass, bool hasStaticOverload, bool cppSelfAsReference) +{ + bool useWrapperClass = avoidProtectedHack() && metaClass->hasProtectedMembers(); + QString className = useWrapperClass ? wrapperName(metaClass) : QString("::%1").arg(metaClass->qualifiedCppName()); + + QString cppSelfAttribution; + if (cppSelfAsReference) { + QString cast = useWrapperClass ? QString("(%1*)").arg(className) : QString(); + cppSelfAttribution = QString("%1& %2 = *(%3%4)") + .arg(className) + .arg(CPP_SELF_VAR) + .arg(cast) + .arg(cpythonWrapperCPtr(metaClass, PYTHON_SELF_VAR)); + } else { + s << INDENT << className << "* " CPP_SELF_VAR " = 0;" << endl; + writeUnusedVariableCast(s, CPP_SELF_VAR); + cppSelfAttribution = QString("%1 = %2%3") + .arg(CPP_SELF_VAR) + .arg(useWrapperClass ? QString("(%1*)").arg(className) : "") + .arg(cpythonWrapperCPtr(metaClass, PYTHON_SELF_VAR)); + } + + // Checks if the underlying C++ object is valid. + if (hasStaticOverload && !cppSelfAsReference) { + s << INDENT << "if (" PYTHON_SELF_VAR ") {" << endl; + { + Indentation indent(INDENT); + writeInvalidPyObjectCheck(s, PYTHON_SELF_VAR); + s << INDENT << cppSelfAttribution << ';' << endl; + } + s << INDENT << '}' << endl; + return; + } + + writeInvalidPyObjectCheck(s, PYTHON_SELF_VAR); + s << INDENT << cppSelfAttribution << ';' << endl; +} + +void CppGenerator::writeCppSelfDefinition(QTextStream& s, const AbstractMetaFunction* func, bool hasStaticOverload) +{ + if (!func->ownerClass() || func->isConstructor()) + return; + + if (func->isOperatorOverload() && func->isBinaryOperator()) { + QString checkFunc = cpythonCheckFunction(func->ownerClass()->typeEntry()); + s << INDENT << "bool isReverse = " << checkFunc << PYTHON_ARG ")" << endl; + { + Indentation indent1(INDENT); + Indentation indent2(INDENT); + Indentation indent3(INDENT); + Indentation indent4(INDENT); + s << INDENT << "&& !" << checkFunc << PYTHON_SELF_VAR ");" << endl; + } + s << INDENT << "if (isReverse)" << endl; + Indentation indent(INDENT); + s << INDENT << "std::swap(" PYTHON_SELF_VAR ", " PYTHON_ARG ");" << endl; + } + + writeCppSelfDefinition(s, func->ownerClass(), hasStaticOverload); +} + +void CppGenerator::writeErrorSection(QTextStream& s, OverloadData& overloadData) +{ + const AbstractMetaFunction* rfunc = overloadData.referenceFunction(); + s << endl << INDENT << cpythonFunctionName(rfunc) << "_TypeError:" << endl; + Indentation indentation(INDENT); + QString funcName = fullPythonFunctionName(rfunc); + + QString argsVar = pythonFunctionWrapperUsesListOfArguments(overloadData) ? "args" : PYTHON_ARG; + if (verboseErrorMessagesDisabled()) { + s << INDENT << "Shiboken::setErrorAboutWrongArguments(" << argsVar << ", \"" << funcName << "\", 0);" << endl; + } else { + QStringList overloadSignatures; + foreach (const AbstractMetaFunction* f, overloadData.overloads()) { + QStringList args; + foreach(AbstractMetaArgument* arg, f->arguments()) { + QString strArg; + AbstractMetaType* argType = arg->type(); + if (isCString(argType)) { + strArg = "\"SBK_STR_NAME\""; + } else if (argType->isPrimitive()) { + const PrimitiveTypeEntry* ptp = reinterpret_cast(argType->typeEntry()); + while (ptp->aliasedTypeEntry()) + ptp = ptp->aliasedTypeEntry(); + strArg = ptp->name(); + if (strArg == "QString") { + strArg = "unicode"; + } else if (strArg == "QChar") { + strArg = "1-unicode"; + } else { + strArg = ptp->name().replace(QRegExp("^signed\\s+"), ""); + if (strArg == "double") + strArg = "float"; + } + } else if (argType->typeEntry()->isContainer()) { + strArg = argType->fullName(); + if (strArg == "QList" || strArg == "QVector" + || strArg == "QLinkedList" || strArg == "QStack" + || strArg == "QQueue") { + strArg = "list"; + } else if (strArg == "QMap" || strArg == "QHash" + || strArg == "QMultiMap" || strArg == "QMultiHash") { + strArg = "dict"; + } else if (strArg == "QPair") { + strArg == "2-tuple"; + } + } else { + strArg = argType->fullName(); + if (strArg == "PyUnicode") + strArg = "unicode"; + else if (strArg == "PyString") + strArg = "str"; + else if (strArg == "PyBytes") + strArg = "\"SBK_STR_NAME\""; + else if (strArg == "PySequece") + strArg = "list"; + else if (strArg == "PyTuple") + strArg = "tuple"; + else if (strArg == "PyDict") + strArg = "dict"; + else if (strArg == "PyObject") + strArg = "object"; + else if (strArg == "PyCallable") + strArg = "callable"; + else if (strArg == "uchar") + strArg = "buffer"; // This depends on an inject code to be true, but if it's not true + // the function wont work at all, so it must be true. + } + if (!arg->defaultValueExpression().isEmpty()) { + strArg += " = "; + if ((isCString(argType) || isPointerToWrapperType(argType)) + && arg->defaultValueExpression() == "0") { + strArg += "None"; + } else { + strArg += arg->defaultValueExpression().replace("::", ".").replace("\"", "\\\""); + } + } + args << strArg; + } + overloadSignatures << "\""+args.join(", ")+"\""; + } + s << INDENT << "const char* overloads[] = {" << overloadSignatures.join(", ") << ", 0};" << endl; + s << INDENT << "Shiboken::setErrorAboutWrongArguments(" << argsVar << ", \"" << funcName << "\", overloads);" << endl; + } + s << INDENT << "return " << m_currentErrorCode << ';' << endl; +} + +void CppGenerator::writeFunctionReturnErrorCheckSection(QTextStream& s, bool hasReturnValue) +{ + s << INDENT << "if (PyErr_Occurred()" << (hasReturnValue ? " || !" PYTHON_RETURN_VAR : "") << ") {" << endl; + { + Indentation indent(INDENT); + if (hasReturnValue) + s << INDENT << "Py_XDECREF(" PYTHON_RETURN_VAR ");" << endl; + s << INDENT << "return " << m_currentErrorCode << ';' << endl; + } + s << INDENT << '}' << endl; +} + +void CppGenerator::writeInvalidPyObjectCheck(QTextStream& s, const QString& pyObj) +{ + s << INDENT << "if (!Shiboken::Object::isValid(" << pyObj << "))" << endl; + Indentation indent(INDENT); + s << INDENT << "return " << m_currentErrorCode << ';' << endl; +} + +static QString pythonToCppConverterForArgumentName(const QString& argumentName) +{ + static QRegExp pyArgsRegex(PYTHON_ARGS"(\\[\\d+[-]?\\d*\\])"); + pyArgsRegex.indexIn(argumentName); + return QString(PYTHON_TO_CPP_VAR"%1").arg(pyArgsRegex.cap(1)); +} + +void CppGenerator::writeTypeCheck(QTextStream& s, const AbstractMetaType* argType, QString argumentName, bool isNumber, QString customType, bool rejectNull) +{ + QString customCheck; + if (!customType.isEmpty()) { + AbstractMetaType* metaType; + customCheck = guessCPythonCheckFunction(customType, &metaType); + if (metaType) + argType = metaType; + } + + // TODO-CONVERTER: merge this with the code below. + QString typeCheck; + if (customCheck.isEmpty()) + typeCheck = cpythonIsConvertibleFunction(argType, argType->isEnum() ? false : isNumber); + else + typeCheck = customCheck; + typeCheck.append(QString("(%1)").arg(argumentName)); + + // TODO-CONVERTER ----------------------------------------------------------------------- + if (customCheck.isEmpty() && !argType->typeEntry()->isCustom()) { + typeCheck = QString("(%1 = %2))").arg(pythonToCppConverterForArgumentName(argumentName)).arg(typeCheck); + if (!isNumber && argType->typeEntry()->isCppPrimitive()) + typeCheck.prepend(QString("%1(%2) && ").arg(cpythonCheckFunction(argType)).arg(argumentName)); + } + // TODO-CONVERTER ----------------------------------------------------------------------- + + if (rejectNull) + typeCheck = QString("(%1 != Py_None && %2)").arg(argumentName).arg(typeCheck); + + s << typeCheck; +} + +static void checkTypeViability(const AbstractMetaFunction* func, const AbstractMetaType* type, int argIdx) +{ + if (!type + || !type->typeEntry()->isPrimitive() + || type->indirections() == 0 + || ShibokenGenerator::isCString(type) + || func->argumentRemoved(argIdx) + || !func->typeReplaced(argIdx).isEmpty() + || !func->conversionRule(TypeSystem::All, argIdx).isEmpty() + || func->hasInjectedCode()) + return; + QString prefix; + if (func->ownerClass()) + prefix = QString("%1::").arg(func->ownerClass()->qualifiedCppName()); + ReportHandler::warning(QString("There's no user provided way (conversion rule, argument removal, custom code, etc) " + "to handle the primitive %1 type '%2' in function '%3%4'.") + .arg(argIdx == 0 ? "return" : "argument") + .arg(type->cppSignature()) + .arg(prefix) + .arg(func->signature())); +} + +static void checkTypeViability(const AbstractMetaFunction* func) +{ + if (func->isUserAdded()) + return; + const AbstractMetaType* type = func->type(); + checkTypeViability(func, type, 0); + for (int i = 0; i < func->arguments().count(); ++i) + checkTypeViability(func, func->arguments().at(i)->type(), i + 1); +} + +void CppGenerator::writeTypeCheck(QTextStream& s, const OverloadData* overloadData, QString argumentName) +{ + QSet numericTypes; + + foreach (OverloadData* od, overloadData->previousOverloadData()->nextOverloadData()) { + foreach (const AbstractMetaFunction* func, od->overloads()) { + checkTypeViability(func); + const AbstractMetaType* argType = od->argument(func)->type(); + if (!argType->isPrimitive()) + continue; + if (ShibokenGenerator::isNumber(argType->typeEntry())) + numericTypes << argType->typeEntry(); + } + } + + // This condition trusts that the OverloadData object will arrange for + // PyInt type to come after the more precise numeric types (e.g. float and bool) + const AbstractMetaType* argType = overloadData->argType(); + bool numberType = numericTypes.count() == 1 || ShibokenGenerator::isPyInt(argType); + QString customType = (overloadData->hasArgumentTypeReplace() ? overloadData->argumentTypeReplaced() : ""); + bool rejectNull = shouldRejectNullPointerArgument(overloadData->referenceFunction(), overloadData->argPos()); + writeTypeCheck(s, argType, argumentName, numberType, customType, rejectNull); +} + +void CppGenerator::writeArgumentConversion(QTextStream& s, + const AbstractMetaType* argType, + const QString& argName, const QString& pyArgName, + const AbstractMetaClass* context, + const QString& defaultValue, + bool castArgumentAsUnused) +{ + if (argType->typeEntry()->isCustom() || argType->typeEntry()->isVarargs()) + return; + if (isWrapperType(argType)) + writeInvalidPyObjectCheck(s, pyArgName); + writePythonToCppTypeConversion(s, argType, pyArgName, argName, context, defaultValue); + if (castArgumentAsUnused) + writeUnusedVariableCast(s, argName); +} + +const AbstractMetaType* CppGenerator::getArgumentType(const AbstractMetaFunction* func, int argPos) +{ + if (argPos < 0 || argPos > func->arguments().size()) { + ReportHandler::warning(QString("Argument index for function '%1' out of range.").arg(func->signature())); + return 0; + } + + const AbstractMetaType* argType = 0; + QString typeReplaced = func->typeReplaced(argPos); + if (typeReplaced.isEmpty()) + argType = (argPos == 0) ? func->type() : func->arguments().at(argPos-1)->type(); + else + argType = buildAbstractMetaTypeFromString(typeReplaced); + if (!argType && !m_knownPythonTypes.contains(typeReplaced)) { + ReportHandler::warning(QString("Unknown type '%1' used as argument type replacement "\ + "in function '%2', the generated code may be broken.") + .arg(typeReplaced) + .arg(func->signature())); + } + return argType; +} + +void CppGenerator::writePythonToCppTypeConversion(QTextStream& s, + const AbstractMetaType* type, + const QString& pyIn, + const QString& cppOut, + const AbstractMetaClass* context, + const QString& defaultValue) +{ + const TypeEntry* typeEntry = type->typeEntry(); + if (typeEntry->isCustom() || typeEntry->isVarargs()) + return; + + QString cppOutAux = QString("%1_local").arg(cppOut); + + bool treatAsPointer = isValueTypeWithCopyConstructorOnly(type); + bool isPointerOrObjectType = (isObjectType(type) || isPointer(type)) && !isUserPrimitive(type) && !isCppPrimitive(type); + bool isNotContainerEnumOrFlags = !typeEntry->isContainer() && !typeEntry->isEnum() && !typeEntry->isFlags(); + bool mayHaveImplicitConversion = type->isReference() + && !isUserPrimitive(type) + && !isCppPrimitive(type) + && isNotContainerEnumOrFlags + && !(treatAsPointer || isPointerOrObjectType); + QString typeName = getFullTypeNameWithoutModifiers(type); + + bool isProtectedEnum = false; + + if (mayHaveImplicitConversion) { + s << INDENT << typeName << ' ' << cppOutAux; + writeMinimalConstructorExpression(s, type, defaultValue); + s << ';' << endl; + } else if (avoidProtectedHack() && type->typeEntry()->isEnum()) { + const AbstractMetaEnum* metaEnum = findAbstractMetaEnum(type); + if (metaEnum && metaEnum->isProtected()) { + typeName = "long"; + isProtectedEnum = true; + } + } + + s << INDENT << typeName; + if (treatAsPointer || isPointerOrObjectType) { + s << "* " << cppOut << (defaultValue.isEmpty() ? "" : QString(" = %1").arg(defaultValue)); + } else if (type->isReference() && !typeEntry->isPrimitive() && isNotContainerEnumOrFlags) { + s << "* " << cppOut << " = &" << cppOutAux; + } else { + s << ' ' << cppOut; + if (isProtectedEnum && avoidProtectedHack()) { + s << " = "; + if (defaultValue.isEmpty()) + s << "0"; + else + s << "(long)" << defaultValue; + } else if (isUserPrimitive(type) || typeEntry->isEnum() || typeEntry->isFlags()) { + writeMinimalConstructorExpression(s, typeEntry, defaultValue); + } else if (!type->isContainer()) { + writeMinimalConstructorExpression(s, type, defaultValue); + } + } + s << ';' << endl; + + QString pythonToCppFunc = pythonToCppConverterForArgumentName(pyIn); + + s << INDENT; + if (!defaultValue.isEmpty()) + s << "if (" << pythonToCppFunc << ") "; + + QString pythonToCppCall = QString("%1(%2, &%3)").arg(pythonToCppFunc).arg(pyIn).arg(cppOut); + if (!mayHaveImplicitConversion) { + s << pythonToCppCall << ';' << endl; + return; + } + + if (!defaultValue.isEmpty()) + s << '{' << endl << INDENT; + + s << "if (Shiboken::Conversions::isImplicitConversion((SbkObjectType*)"; + s << cpythonTypeNameExt(type) << ", " << pythonToCppFunc << "))" << endl; + { + Indentation indent(INDENT); + s << INDENT << pythonToCppFunc << '(' << pyIn << ", &" << cppOutAux << ");" << endl; + } + s << INDENT << "else" << endl; + { + Indentation indent(INDENT); + s << INDENT << pythonToCppCall << ';' << endl; + } + + if (!defaultValue.isEmpty()) + s << INDENT << '}'; + s << endl; +} + +static void addConversionRuleCodeSnippet(CodeSnipList& snippetList, QString& rule, + TypeSystem::Language conversionLanguage, + TypeSystem::Language snippetLanguage, + QString outputName = QString(), + QString inputName = QString()) +{ + if (rule.isEmpty()) + return; + if (snippetLanguage == TypeSystem::TargetLangCode) { + rule.replace("%in", inputName); + rule.replace("%out", QString("%1_out").arg(outputName)); + } else { + rule.replace("%out", outputName); + } + CodeSnip snip(0, snippetLanguage); + snip.position = (snippetLanguage == TypeSystem::NativeCode) ? CodeSnip::Any : CodeSnip::Beginning; + snip.addCode(rule); + snippetList << snip; +} + +void CppGenerator::writeConversionRule(QTextStream& s, const AbstractMetaFunction* func, TypeSystem::Language language) +{ + CodeSnipList snippets; + foreach (AbstractMetaArgument* arg, func->arguments()) { + QString rule = func->conversionRule(language, arg->argumentIndex() + 1); + addConversionRuleCodeSnippet(snippets, rule, language, TypeSystem::TargetLangCode, + arg->name(), arg->name()); + } + writeCodeSnips(s, snippets, CodeSnip::Beginning, TypeSystem::TargetLangCode, func); +} + +void CppGenerator::writeConversionRule(QTextStream& s, const AbstractMetaFunction* func, TypeSystem::Language language, const QString& outputVar) +{ + CodeSnipList snippets; + QString rule = func->conversionRule(language, 0); + addConversionRuleCodeSnippet(snippets, rule, language, language, outputVar); + writeCodeSnips(s, snippets, CodeSnip::Any, language, func); +} + +void CppGenerator::writeNoneReturn(QTextStream& s, const AbstractMetaFunction* func, bool thereIsReturnValue) +{ + if (thereIsReturnValue && (!func->type() || func->argumentRemoved(0)) && !injectedCodeHasReturnValueAttribution(func)) { + s << INDENT << PYTHON_RETURN_VAR " = Py_None;" << endl; + s << INDENT << "Py_INCREF(Py_None);" << endl; + } +} + +void CppGenerator::writeOverloadedFunctionDecisor(QTextStream& s, const OverloadData& overloadData) +{ + s << INDENT << "// Overloaded function decisor" << endl; + const AbstractMetaFunction* rfunc = overloadData.referenceFunction(); + QList functionOverloads = overloadData.overloadsWithoutRepetition(); + for (int i = 0; i < functionOverloads.count(); i++) + s << INDENT << "// " << i << ": " << functionOverloads.at(i)->minimalSignature() << endl; + writeOverloadedFunctionDecisorEngine(s, &overloadData); + s << endl; + + // Ensure that the direct overload that called this reverse + // is called. + if (rfunc->isOperatorOverload() && !rfunc->isCallOperator()) { + s << INDENT << "if (isReverse && overloadId == -1) {" << endl; + { + Indentation indent(INDENT); + s << INDENT << "PyErr_SetString(PyExc_NotImplementedError, \"reverse operator not implemented.\");" << endl; + s << INDENT << "return 0;" << endl; + } + s << INDENT << "}" << endl << endl; + } + + s << INDENT << "// Function signature not found." << endl; + s << INDENT << "if (overloadId == -1) goto " << cpythonFunctionName(overloadData.referenceFunction()) << "_TypeError;" << endl; + s << endl; +} + +void CppGenerator::writeOverloadedFunctionDecisorEngine(QTextStream& s, const OverloadData* parentOverloadData) +{ + bool hasDefaultCall = parentOverloadData->nextArgumentHasDefaultValue(); + const AbstractMetaFunction* referenceFunction = parentOverloadData->referenceFunction(); + + // If the next argument has not an argument with a default value, it is still possible + // that one of the overloads for the current overload data has its final occurrence here. + // If found, the final occurrence of a method is attributed to the referenceFunction + // variable to be used further on this method on the conditional that identifies default + // method calls. + if (!hasDefaultCall) { + foreach (const AbstractMetaFunction* func, parentOverloadData->overloads()) { + if (parentOverloadData->isFinalOccurrence(func)) { + referenceFunction = func; + hasDefaultCall = true; + break; + } + } + } + + int maxArgs = parentOverloadData->maxArgs(); + // Python constructors always receive multiple arguments. + bool usePyArgs = pythonFunctionWrapperUsesListOfArguments(*parentOverloadData); + + // Functions without arguments are identified right away. + if (maxArgs == 0) { + s << INDENT << "overloadId = " << parentOverloadData->headOverloadData()->overloads().indexOf(referenceFunction); + s << "; // " << referenceFunction->minimalSignature() << endl; + return; + + // To decide if a method call is possible at this point the current overload + // data object cannot be the head, since it is just an entry point, or a root, + // for the tree of arguments and it does not represent a valid method call. + } else if (!parentOverloadData->isHeadOverloadData()) { + bool isLastArgument = parentOverloadData->nextOverloadData().isEmpty(); + bool signatureFound = parentOverloadData->overloads().size() == 1; + + // The current overload data describes the last argument of a signature, + // so the method can be identified right now. + if (isLastArgument || (signatureFound && !hasDefaultCall)) { + const AbstractMetaFunction* func = parentOverloadData->referenceFunction(); + s << INDENT << "overloadId = " << parentOverloadData->headOverloadData()->overloads().indexOf(func); + s << "; // " << func->minimalSignature() << endl; + return; + } + } + + bool isFirst = true; + + // If the next argument has a default value the decisor can perform a method call; + // it just need to check if the number of arguments received from Python are equal + // to the number of parameters preceding the argument with the default value. + if (hasDefaultCall) { + isFirst = false; + int numArgs = parentOverloadData->argPos() + 1; + s << INDENT << "if (numArgs == " << numArgs << ") {" << endl; + { + Indentation indent(INDENT); + const AbstractMetaFunction* func = referenceFunction; + foreach (OverloadData* overloadData, parentOverloadData->nextOverloadData()) { + const AbstractMetaFunction* defValFunc = overloadData->getFunctionWithDefaultValue(); + if (defValFunc) { + func = defValFunc; + break; + } + } + s << INDENT << "overloadId = " << parentOverloadData->headOverloadData()->overloads().indexOf(func); + s << "; // " << func->minimalSignature() << endl; + } + s << INDENT << '}'; + } + + foreach (OverloadData* overloadData, parentOverloadData->nextOverloadData()) { + bool signatureFound = overloadData->overloads().size() == 1 + && !overloadData->getFunctionWithDefaultValue() + && !overloadData->findNextArgWithDefault(); + + const AbstractMetaFunction* refFunc = overloadData->referenceFunction(); + + QStringList typeChecks; + QString pyArgName = (usePyArgs && maxArgs > 1) ? QString(PYTHON_ARGS "[%1]").arg(overloadData->argPos()) : PYTHON_ARG; + OverloadData* od = overloadData; + int startArg = od->argPos(); + int sequenceArgCount = 0; + while (od && !od->argType()->isVarargs()) { + bool typeReplacedByPyObject = od->argumentTypeReplaced() == "PyObject"; + if (!typeReplacedByPyObject) { + if (usePyArgs) + pyArgName = QString(PYTHON_ARGS "[%1]").arg(od->argPos()); + QString typeCheck; + QTextStream tck(&typeCheck); + const AbstractMetaFunction* func = od->referenceFunction(); + + if (func->isConstructor() && func->arguments().count() == 1) { + const AbstractMetaClass* ownerClass = func->ownerClass(); + const ComplexTypeEntry* baseContainerType = ownerClass->typeEntry()->baseContainerType(); + if (baseContainerType && baseContainerType == func->arguments().first()->type()->typeEntry() && isCopyable(ownerClass)) { + tck << '!' << cpythonCheckFunction(ownerClass->typeEntry()) << pyArgName << ')' << endl; + Indentation indent(INDENT); + tck << INDENT << "&& "; + } + } + writeTypeCheck(tck, od, pyArgName); + typeChecks << typeCheck; + } + + sequenceArgCount++; + + if (od->nextOverloadData().isEmpty() + || od->nextArgumentHasDefaultValue() + || od->nextOverloadData().size() != 1 + || od->overloads().size() != od->nextOverloadData().first()->overloads().size()) { + overloadData = od; + od = 0; + } else { + od = od->nextOverloadData().first(); + } + } + + if (usePyArgs && signatureFound) { + AbstractMetaArgumentList args = refFunc->arguments(); + int lastArgIsVarargs = (int) (args.size() > 1 && args.last()->type()->isVarargs()); + int numArgs = args.size() - OverloadData::numberOfRemovedArguments(refFunc) - lastArgIsVarargs; + typeChecks.prepend(QString("numArgs %1 %2").arg(lastArgIsVarargs ? ">=" : "==").arg(numArgs)); + } else if (sequenceArgCount > 1) { + typeChecks.prepend(QString("numArgs >= %1").arg(startArg + sequenceArgCount)); + } else if (refFunc->isOperatorOverload() && !refFunc->isCallOperator()) { + typeChecks.prepend(QString("%1isReverse").arg(refFunc->isReverseOperator() ? "" : "!")); + } + + if (isFirst) { + isFirst = false; + s << INDENT; + } else { + s << " else "; + } + s << "if ("; + if (typeChecks.isEmpty()) { + s << "true"; + } else { + Indentation indent(INDENT); + QString separator; + QTextStream sep(&separator); + sep << endl << INDENT << "&& "; + s << typeChecks.join(separator); + } + s << ") {" << endl; + { + Indentation indent(INDENT); + writeOverloadedFunctionDecisorEngine(s, overloadData); + } + s << INDENT << "}"; + } + s << endl; +} + +void CppGenerator::writeFunctionCalls(QTextStream& s, const OverloadData& overloadData) +{ + QList overloads = overloadData.overloadsWithoutRepetition(); + s << INDENT << "// Call function/method" << endl; + s << INDENT << (overloads.count() > 1 ? "switch (overloadId) " : "") << '{' << endl; + { + Indentation indent(INDENT); + if (overloads.count() == 1) { + writeSingleFunctionCall(s, overloadData, overloads.first()); + } else { + for (int i = 0; i < overloads.count(); i++) { + const AbstractMetaFunction* func = overloads.at(i); + s << INDENT << "case " << i << ": // " << func->signature() << endl; + s << INDENT << '{' << endl; + { + Indentation indent(INDENT); + writeSingleFunctionCall(s, overloadData, func); + s << INDENT << "break;" << endl; + } + s << INDENT << '}' << endl; + } + } + } + s << INDENT << '}' << endl; +} + +void CppGenerator::writeSingleFunctionCall(QTextStream& s, const OverloadData& overloadData, const AbstractMetaFunction* func) +{ + if (func->isDeprecated()) { + s << INDENT << "Shiboken::warning(PyExc_DeprecationWarning, 1, \"Function: '" + << func->signature().replace("::", ".") + << "' is marked as deprecated, please check the documentation for more information.\");" << endl; + } + + if (func->functionType() == AbstractMetaFunction::EmptyFunction) { + s << INDENT << "PyErr_Format(PyExc_TypeError, \"%s is a private method.\", \"" << func->signature().replace("::", ".") << "\");" << endl; + s << INDENT << "return " << m_currentErrorCode << ';' << endl; + return; + } + + bool usePyArgs = pythonFunctionWrapperUsesListOfArguments(overloadData); + + // Handle named arguments. + writeNamedArgumentResolution(s, func, usePyArgs); + + bool injectCodeCallsFunc = injectedCodeCallsCppFunction(func); + bool mayHaveUnunsedArguments = !func->isUserAdded() && func->hasInjectedCode() && injectCodeCallsFunc; + int removedArgs = 0; + for (int argIdx = 0; argIdx < func->arguments().count(); ++argIdx) { + bool hasConversionRule = !func->conversionRule(TypeSystem::NativeCode, argIdx + 1).isEmpty(); + const AbstractMetaArgument* arg = func->arguments().at(argIdx); + if (func->argumentRemoved(argIdx + 1)) { + if (!arg->defaultValueExpression().isEmpty()) { + QString cppArgRemoved = QString(CPP_ARG_REMOVED"%1").arg(argIdx); + s << INDENT << getFullTypeName(arg->type()) << ' ' << cppArgRemoved; + s << " = " << guessScopeForDefaultValue(func, arg) << ';' << endl; + writeUnusedVariableCast(s, cppArgRemoved); + } else if (!injectCodeCallsFunc && !func->isUserAdded() && !hasConversionRule) { + // When an argument is removed from a method signature and no other means of calling + // the method are provided (as with code injection) the generator must abort. + qFatal(qPrintable(QString("No way to call '%1::%2' with the modifications described in the type system.") + .arg(func->ownerClass()->name()) + .arg(func->signature())), NULL); + } + removedArgs++; + continue; + } + if (hasConversionRule) + continue; + const AbstractMetaType* argType = getArgumentType(func, argIdx + 1); + if (!argType || (mayHaveUnunsedArguments && !injectedCodeUsesArgument(func, argIdx))) + continue; + int argPos = argIdx - removedArgs; + QString argName = QString(CPP_ARG"%1").arg(argPos); + QString pyArgName = usePyArgs ? QString(PYTHON_ARGS "[%1]").arg(argPos) : PYTHON_ARG; + QString defaultValue = guessScopeForDefaultValue(func, arg); + writeArgumentConversion(s, argType, argName, pyArgName, func->implementingClass(), defaultValue, func->isUserAdded()); + } + + s << endl; + + int numRemovedArgs = OverloadData::numberOfRemovedArguments(func); + + s << INDENT << "if (!PyErr_Occurred()) {" << endl; + { + Indentation indentation(INDENT); + writeMethodCall(s, func, func->arguments().size() - numRemovedArgs); + if (!func->isConstructor()) + writeNoneReturn(s, func, overloadData.hasNonVoidReturnType()); + } + s << INDENT << '}' << endl; +} + +QString CppGenerator::cppToPythonFunctionName(const QString& sourceTypeName, QString targetTypeName) +{ + if (targetTypeName.isEmpty()) + targetTypeName = sourceTypeName; + return QString("%1_CppToPython_%2").arg(sourceTypeName).arg(targetTypeName); +} + +QString CppGenerator::pythonToCppFunctionName(const QString& sourceTypeName, const QString& targetTypeName) +{ + return QString("%1_PythonToCpp_%2").arg(sourceTypeName).arg(targetTypeName); +} +QString CppGenerator::pythonToCppFunctionName(const AbstractMetaType* sourceType, const AbstractMetaType* targetType) +{ + return pythonToCppFunctionName(fixedCppTypeName(sourceType), fixedCppTypeName(targetType)); +} +QString CppGenerator::pythonToCppFunctionName(const CustomConversion::TargetToNativeConversion* toNative, + const TypeEntry* targetType) +{ + return pythonToCppFunctionName(fixedCppTypeName(toNative), fixedCppTypeName(targetType)); +} + +QString CppGenerator::convertibleToCppFunctionName(const QString& sourceTypeName, const QString& targetTypeName) +{ + return QString("is_%1_PythonToCpp_%2_Convertible").arg(sourceTypeName).arg(targetTypeName); +} +QString CppGenerator::convertibleToCppFunctionName(const AbstractMetaType* sourceType, const AbstractMetaType* targetType) +{ + return convertibleToCppFunctionName(fixedCppTypeName(sourceType), fixedCppTypeName(targetType)); +} +QString CppGenerator::convertibleToCppFunctionName(const CustomConversion::TargetToNativeConversion* toNative, + const TypeEntry* targetType) +{ + return convertibleToCppFunctionName(fixedCppTypeName(toNative), fixedCppTypeName(targetType)); +} + +void CppGenerator::writeCppToPythonFunction(QTextStream& s, const QString& code, const QString& sourceTypeName, QString targetTypeName) +{ + QString prettyCode; + QTextStream c(&prettyCode); + formatCode(c, code, INDENT); + processCodeSnip(prettyCode); + + s << "static PyObject* " << cppToPythonFunctionName(sourceTypeName, targetTypeName); + s << "(const void* cppIn) {" << endl; + s << prettyCode; + s << '}' << endl; +} + +static void replaceCppToPythonVariables(QString& code, const QString& typeName) +{ + code.prepend(QString("%1& cppInRef = *((%1*)cppIn);\n").arg(typeName)); + code.replace("%INTYPE", typeName); + code.replace("%OUTTYPE", "PyObject*"); + code.replace("%in", "cppInRef"); + code.replace("%out", "pyOut"); +} +void CppGenerator::writeCppToPythonFunction(QTextStream& s, const CustomConversion* customConversion) +{ + QString code = customConversion->nativeToTargetConversion(); + replaceCppToPythonVariables(code, getFullTypeName(customConversion->ownerType())); + writeCppToPythonFunction(s, code, fixedCppTypeName(customConversion->ownerType())); +} +void CppGenerator::writeCppToPythonFunction(QTextStream& s, const AbstractMetaType* containerType) +{ + const CustomConversion* customConversion = containerType->typeEntry()->customConversion(); + if (!customConversion) { + qFatal(qPrintable(QString("Can't write the C++ to Python conversion function for container type '%1' - "\ + "no conversion rule was defined for it in the type system.") + .arg(containerType->typeEntry()->qualifiedCppName())), NULL); + } + if (!containerType->typeEntry()->isContainer()) { + writeCppToPythonFunction(s, customConversion); + return; + } + QString code = customConversion->nativeToTargetConversion(); + for (int i = 0; i < containerType->instantiations().count(); ++i) + code.replace(QString("%INTYPE_%1").arg(i), getFullTypeName(containerType->instantiations().at(i))); + replaceCppToPythonVariables(code, getFullTypeNameWithoutModifiers(containerType)); + processCodeSnip(code); + writeCppToPythonFunction(s, code, fixedCppTypeName(containerType)); +} + +void CppGenerator::writePythonToCppFunction(QTextStream& s, const QString& code, const QString& sourceTypeName, const QString& targetTypeName) +{ + QString prettyCode; + QTextStream c(&prettyCode); + formatCode(c, code, INDENT); + processCodeSnip(prettyCode); + s << "static void " << pythonToCppFunctionName(sourceTypeName, targetTypeName); + s << "(PyObject* pyIn, void* cppOut) {" << endl; + s << prettyCode; + s << '}' << endl; +} + +void CppGenerator::writeIsPythonConvertibleToCppFunction(QTextStream& s, + const QString& sourceTypeName, + const QString& targetTypeName, + const QString& condition, + QString pythonToCppFuncName, + bool acceptNoneAsCppNull) +{ + if (pythonToCppFuncName.isEmpty()) + pythonToCppFuncName = pythonToCppFunctionName(sourceTypeName, targetTypeName); + + s << "static PythonToCppFunc " << convertibleToCppFunctionName(sourceTypeName, targetTypeName); + s << "(PyObject* pyIn) {" << endl; + if (acceptNoneAsCppNull) { + s << INDENT << "if (pyIn == Py_None)" << endl; + Indentation indent(INDENT); + s << INDENT << "return Shiboken::Conversions::nonePythonToCppNullPtr;" << endl; + } + s << INDENT << "if (" << condition << ')' << endl; + { + Indentation indent(INDENT); + s << INDENT << "return " << pythonToCppFuncName << ';' << endl; + } + s << INDENT << "return 0;" << endl; + s << '}' << endl; +} + +void CppGenerator::writePythonToCppConversionFunctions(QTextStream& s, + const AbstractMetaType* sourceType, + const AbstractMetaType* targetType, + QString typeCheck, + QString conversion, + QString preConversion) +{ + QString sourcePyType = cpythonTypeNameExt(sourceType); + + // Python to C++ conversion function. + QString code; + QTextStream c(&code); + if (conversion.isEmpty()) + conversion = QString("*%1").arg(cpythonWrapperCPtr(sourceType->typeEntry(), "pyIn")); + if (!preConversion.isEmpty()) + c << INDENT << preConversion << endl; + c << INDENT << QString("*((%1*)cppOut) = %1(%2);") + .arg(getFullTypeName(targetType->typeEntry())) + .arg(conversion); + QString sourceTypeName = fixedCppTypeName(sourceType); + QString targetTypeName = fixedCppTypeName(targetType); + writePythonToCppFunction(s, code, sourceTypeName, targetTypeName); + + // Python to C++ convertible check function. + if (typeCheck.isEmpty()) + typeCheck = QString("PyObject_TypeCheck(pyIn, %1)").arg(sourcePyType); + writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, typeCheck); + s << endl; +} + +void CppGenerator::writePythonToCppConversionFunctions(QTextStream& s, + const CustomConversion::TargetToNativeConversion* toNative, + const TypeEntry* targetType) +{ + // Python to C++ conversion function. + QString code = toNative->conversion(); + QString inType; + if (toNative->sourceType()) + inType = cpythonTypeNameExt(toNative->sourceType()); + else + inType = QString("(&%1_Type)").arg(toNative->sourceTypeName()); + code.replace("%INTYPE", inType); + code.replace("%OUTTYPE", targetType->qualifiedCppName()); + code.replace("%in", "pyIn"); + code.replace("%out", QString("*((%1*)cppOut)").arg(getFullTypeName(targetType))); + + QString sourceTypeName = fixedCppTypeName(toNative); + QString targetTypeName = fixedCppTypeName(targetType); + writePythonToCppFunction(s, code, sourceTypeName, targetTypeName); + + // Python to C++ convertible check function. + QString typeCheck = toNative->sourceTypeCheck(); + if (typeCheck.isEmpty()) { + QString pyTypeName = toNative->sourceTypeName(); + if (pyTypeName == "Py_None" || pyTypeName == "PyNone") + typeCheck = "%in == Py_None"; + else if (pyTypeName == "SbkEnumType") + typeCheck = "Shiboken::isShibokenEnum(%in)"; + else if (pyTypeName == "SbkObject") + typeCheck = "Shiboken::Object::checkType(%in)"; + else if (pyTypeName == "PyTypeObject") + typeCheck = "PyType_Check(%in)"; + else if (pyTypeName == "PyObject") + typeCheck = "PyObject_TypeCheck(%in, &PyBaseObject_Type)"; + else if (pyTypeName.startsWith("Py")) + typeCheck= QString("%1_Check(%in)").arg(pyTypeName); + } + if (typeCheck.isEmpty()) { + if (!toNative->sourceType() || toNative->sourceType()->isPrimitive()) { + qFatal(qPrintable(QString("User added implicit conversion for C++ type '%1' must provide either an input "\ + "type check function or a non primitive type entry.") + .arg(targetType->qualifiedCppName())), NULL); + + } + typeCheck = QString("PyObject_TypeCheck(%in, %1)").arg(cpythonTypeNameExt(toNative->sourceType())); + } + typeCheck.replace("%in", "pyIn"); + processCodeSnip(typeCheck); + writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, typeCheck); +} + +void CppGenerator::writePythonToCppConversionFunctions(QTextStream& s, const AbstractMetaType* containerType) +{ + const CustomConversion* customConversion = containerType->typeEntry()->customConversion(); + if (!customConversion) { + //qFatal + return; + } + const CustomConversion::TargetToNativeConversions& toCppConversions = customConversion->targetToNativeConversions(); + if (toCppConversions.isEmpty()) { + //qFatal + return; + } + // Python to C++ conversion function. + QString cppTypeName = getFullTypeNameWithoutModifiers(containerType); + QString code; + QTextStream c(&code); + c << INDENT << QString("%1& cppOutRef = *((%1*)cppOut);").arg(cppTypeName) << endl; + code.append(toCppConversions.first()->conversion()); + for (int i = 0; i < containerType->instantiations().count(); ++i) { + const AbstractMetaType* type = containerType->instantiations().at(i); + QString typeName = getFullTypeName(type); + if (type->isValue() && isValueTypeWithCopyConstructorOnly(type)) { + static QRegExp regex(CONVERTTOCPP_REGEX); + int pos = 0; + while ((pos = regex.indexIn(code, pos)) != -1) { + pos += regex.matchedLength(); + QStringList list = regex.capturedTexts(); + QString varName = list.at(1); + QString leftCode = code.left(pos); + QString rightCode = code.mid(pos); + rightCode.replace(varName, "*"+varName); + code = leftCode + rightCode; + } + typeName.append('*'); + } + code.replace(QString("%OUTTYPE_%1").arg(i), typeName); + } + code.replace("%OUTTYPE", cppTypeName); + code.replace("%in", "pyIn"); + code.replace("%out", "cppOutRef"); + QString typeName = fixedCppTypeName(containerType); + writePythonToCppFunction(s, code, typeName, typeName); + + // Python to C++ convertible check function. + QString typeCheck = cpythonCheckFunction(containerType); + if (typeCheck.isEmpty()) + typeCheck = "false"; + else + typeCheck = QString("%1pyIn)").arg(typeCheck); + writeIsPythonConvertibleToCppFunction(s, typeName, typeName, typeCheck); + s << endl; +} + +void CppGenerator::writeAddPythonToCppConversion(QTextStream& s, const QString& converterVar, const QString& pythonToCppFunc, const QString& isConvertibleFunc) +{ + s << INDENT << "Shiboken::Conversions::addPythonToCppValueConversion(" << converterVar << ',' << endl; + { + Indentation indent(INDENT); + s << INDENT << pythonToCppFunc << ',' << endl; + s << INDENT << isConvertibleFunc; + } + s << ");" << endl; +} + +void CppGenerator::writeNamedArgumentResolution(QTextStream& s, const AbstractMetaFunction* func, bool usePyArgs) +{ + AbstractMetaArgumentList args = OverloadData::getArgumentsWithDefaultValues(func); + if (args.isEmpty()) + return; + + QString pyErrString("PyErr_SetString(PyExc_TypeError, \"" + fullPythonFunctionName(func) + + "(): got multiple values for keyword argument '%1'.\");"); + + s << INDENT << "if (kwds) {" << endl; + { + Indentation indent(INDENT); + s << INDENT << "PyObject* "; + foreach (const AbstractMetaArgument* arg, args) { + int pyArgIndex = arg->argumentIndex() - OverloadData::numberOfRemovedArguments(func, arg->argumentIndex()); + QString pyArgName = usePyArgs ? QString(PYTHON_ARGS "[%1]").arg(pyArgIndex) : PYTHON_ARG; + s << "value = PyDict_GetItemString(kwds, \"" << arg->name() << "\");" << endl; + s << INDENT << "if (value && " << pyArgName << ") {" << endl; + { + Indentation indent(INDENT); + s << INDENT << pyErrString.arg(arg->name()) << endl; + s << INDENT << "return " << m_currentErrorCode << ';' << endl; + } + s << INDENT << "} else if (value) {" << endl; + { + Indentation indent(INDENT); + s << INDENT << pyArgName << " = value;" << endl; + s << INDENT << "if (!"; + writeTypeCheck(s, arg->type(), pyArgName, isNumber(arg->type()->typeEntry()), func->typeReplaced(arg->argumentIndex() + 1)); + s << ')' << endl; + { + Indentation indent(INDENT); + s << INDENT << "goto " << cpythonFunctionName(func) << "_TypeError;" << endl; + } + } + s << INDENT << '}' << endl; + if (arg != args.last()) + s << INDENT; + } + } + s << INDENT << '}' << endl; +} + +QString CppGenerator::argumentNameFromIndex(const AbstractMetaFunction* func, int argIndex, const AbstractMetaClass** wrappedClass) +{ + *wrappedClass = 0; + QString pyArgName; + if (argIndex == -1) { + pyArgName = QString(PYTHON_SELF_VAR); + *wrappedClass = func->implementingClass(); + } else if (argIndex == 0) { + AbstractMetaType* returnType = getTypeWithoutContainer(func->type()); + if (returnType) { + pyArgName = PYTHON_RETURN_VAR; + *wrappedClass = classes().findClass(returnType->typeEntry()->name()); + } else { + ReportHandler::warning("Invalid Argument index on function modification: " + func->name()); + } + } else { + int realIndex = argIndex - 1 - OverloadData::numberOfRemovedArguments(func, argIndex - 1); + AbstractMetaType* argType = getTypeWithoutContainer(func->arguments().at(realIndex)->type()); + + if (argType) { + *wrappedClass = classes().findClass(argType->typeEntry()->name()); + if (argIndex == 1 + && !func->isConstructor() + && OverloadData::isSingleArgument(getFunctionGroups(func->implementingClass())[func->name()])) + pyArgName = QString(PYTHON_ARG); + else + pyArgName = QString(PYTHON_ARGS "[%1]").arg(argIndex - 1); + } + } + return pyArgName; +} + +void CppGenerator::writeMethodCall(QTextStream& s, const AbstractMetaFunction* func, int maxArgs) +{ + s << INDENT << "// " << func->minimalSignature() << (func->isReverseOperator() ? " [reverse operator]": "") << endl; + if (func->isConstructor()) { + foreach (CodeSnip cs, func->injectedCodeSnips()) { + if (cs.position == CodeSnip::End) { + s << INDENT << "overloadId = " << func->ownerClass()->functions().indexOf(const_cast(func)) << ';' << endl; + break; + } + } + } + + if (func->isAbstract()) { + s << INDENT << "if (Shiboken::Object::hasCppWrapper(reinterpret_cast(" PYTHON_SELF_VAR "))) {\n"; + { + Indentation indent(INDENT); + s << INDENT << "PyErr_SetString(PyExc_NotImplementedError, \"pure virtual method '"; + s << func->ownerClass()->name() << '.' << func->name() << "()' not implemented.\");" << endl; + s << INDENT << "return " << m_currentErrorCode << ';' << endl; + } + s << INDENT << "}\n"; + } + + // Used to provide contextual information to custom code writer function. + const AbstractMetaArgument* lastArg = 0; + + CodeSnipList snips; + if (func->hasInjectedCode()) { + snips = func->injectedCodeSnips(); + + // Find the last argument available in the method call to provide + // the injected code writer with information to avoid invalid replacements + // on the %# variable. + if (maxArgs > 0 && maxArgs < func->arguments().size() - OverloadData::numberOfRemovedArguments(func)) { + int removedArgs = 0; + for (int i = 0; i < maxArgs + removedArgs; i++) { + lastArg = func->arguments().at(i); + if (func->argumentRemoved(i + 1)) + removedArgs++; + } + } else if (maxArgs != 0 && !func->arguments().isEmpty()) { + lastArg = func->arguments().last(); + } + + writeCodeSnips(s, snips, CodeSnip::Beginning, TypeSystem::TargetLangCode, func, lastArg); + s << endl; + } + + writeConversionRule(s, func, TypeSystem::NativeCode); + + if (!func->isUserAdded()) { + QStringList userArgs; + if (!func->isCopyConstructor()) { + int removedArgs = 0; + for (int i = 0; i < maxArgs + removedArgs; i++) { + const AbstractMetaArgument* arg = func->arguments().at(i); + bool hasConversionRule = !func->conversionRule(TypeSystem::NativeCode, arg->argumentIndex() + 1).isEmpty(); + if (func->argumentRemoved(i + 1)) { + // If some argument with default value is removed from a + // method signature, the said value must be explicitly + // added to the method call. + removedArgs++; + + // If have conversion rules I will use this for removed args + if (hasConversionRule) + userArgs << QString("%1"CONV_RULE_OUT_VAR_SUFFIX).arg(arg->name()); + else if (!arg->defaultValueExpression().isEmpty()) + userArgs << QString(CPP_ARG_REMOVED"%1").arg(i); + } else { + int idx = arg->argumentIndex() - removedArgs; + bool deRef = isValueTypeWithCopyConstructorOnly(arg->type()) + || isObjectTypeUsedAsValueType(arg->type()) + || (arg->type()->isReference() && isWrapperType(arg->type()) && !isPointer(arg->type())); + QString argName = hasConversionRule + ? QString("%1"CONV_RULE_OUT_VAR_SUFFIX).arg(arg->name()) + : QString("%1"CPP_ARG"%2").arg(deRef ? "*" : "").arg(idx); + userArgs << argName; + } + } + + // If any argument's default value was modified the method must be called + // with this new value whenever the user doesn't pass an explicit value to it. + // Also, any unmodified default value coming after the last user specified + // argument and before the modified argument must be explicitly stated. + QStringList otherArgs; + bool otherArgsModified = false; + bool argsClear = true; + for (int i = func->arguments().size() - 1; i >= maxArgs + removedArgs; i--) { + const AbstractMetaArgument* arg = func->arguments().at(i); + bool defValModified = arg->defaultValueExpression() != arg->originalDefaultValueExpression(); + bool hasConversionRule = !func->conversionRule(TypeSystem::NativeCode, arg->argumentIndex() + 1).isEmpty(); + if (argsClear && !defValModified && !hasConversionRule) + continue; + else + argsClear = false; + otherArgsModified |= defValModified || hasConversionRule || func->argumentRemoved(i + 1); + if (hasConversionRule) + otherArgs.prepend(QString("%1"CONV_RULE_OUT_VAR_SUFFIX).arg(arg->name())); + else + otherArgs.prepend(QString(CPP_ARG_REMOVED"%1").arg(i)); + } + if (otherArgsModified) + userArgs << otherArgs; + } + + bool isCtor = false; + QString methodCall; + QTextStream mc(&methodCall); + QString useVAddr; + QTextStream uva(&useVAddr); + if (func->isOperatorOverload() && !func->isCallOperator()) { + QString firstArg("(*" CPP_SELF_VAR ")"); + if (func->isPointerOperator()) + firstArg.remove(1, 1); // remove the de-reference operator + + QString secondArg(CPP_ARG0); + if (!func->isUnaryOperator() && shouldDereferenceArgumentPointer(func->arguments().first())) { + secondArg.prepend("(*"); + secondArg.append(')'); + } + + if (func->isUnaryOperator()) + std::swap(firstArg, secondArg); + + QString op = func->originalName(); + op = op.right(op.size() - (sizeof("operator")/sizeof(char)-1)); + + if (func->isBinaryOperator()) { + if (func->isReverseOperator()) + std::swap(firstArg, secondArg); + + if (((op == "++") || (op == "--")) && !func->isReverseOperator()) { + s << endl << INDENT << "for(int i=0; i < " << secondArg << "; i++, " << firstArg << op << ");" << endl; + mc << firstArg; + } else { + mc << firstArg << ' ' << op << ' ' << secondArg; + } + } else { + mc << op << ' ' << secondArg; + } + } else if (!injectedCodeCallsCppFunction(func)) { + if (func->isConstructor() || func->isCopyConstructor()) { + isCtor = true; + QString className = wrapperName(func->ownerClass()); + + if (func->isCopyConstructor() && maxArgs == 1) { + mc << "new ::" << className << "(*" << CPP_ARG0 << ')'; + } else { + QString ctorCall = className + '(' + userArgs.join(", ") + ')'; + if (usePySideExtensions() && func->ownerClass()->isQObject()) { + s << INDENT << "void* addr = PySide::nextQObjectMemoryAddr();" << endl; + uva << "if (addr) {" << endl; + { + Indentation indent(INDENT); + + uva << INDENT << "cptr = " << "new (addr) ::" + << ctorCall << ';' << endl + << INDENT + << "PySide::setNextQObjectMemoryAddr(0);" + << endl; + } + uva << INDENT << "} else {" << endl; + { + Indentation indent(INDENT); + + uva << INDENT << "cptr = " << "new ::" + << ctorCall << ';' << endl; + } + uva << INDENT << "}" << endl; + } else { + mc << "new ::" << ctorCall; + } + } + } else { + if (func->ownerClass()) { + if (!avoidProtectedHack() || !func->isProtected()) { + if (func->isStatic()) { + mc << "::" << func->ownerClass()->qualifiedCppName() << "::"; + } else { + if (func->isConstant()) { + if (avoidProtectedHack()) { + mc << "const_castownerClass()->hasProtectedMembers()) + mc << wrapperName(func->ownerClass()); + else + mc << func->ownerClass()->qualifiedCppName(); + mc << "*>(" CPP_SELF_VAR ")->"; + } else { + mc << "const_castownerClass()->qualifiedCppName(); + mc << "*>(" CPP_SELF_VAR ")->"; + } + } else { + mc << CPP_SELF_VAR "->"; + } + } + + if (!func->isAbstract() && func->isVirtual()) + mc << "::%CLASS_NAME::"; + + mc << func->originalName(); + } else { + if (!func->isStatic()) + mc << "((::" << wrapperName(func->ownerClass()) << "*) " << CPP_SELF_VAR << ")->"; + + if (!func->isAbstract()) + mc << (func->isProtected() ? wrapperName(func->ownerClass()) : "::" + func->ownerClass()->qualifiedCppName()) << "::"; + mc << func->originalName() << "_protected"; + } + } else { + mc << func->originalName(); + } + mc << '(' << userArgs.join(", ") << ')'; + if (!func->isAbstract() && func->isVirtual()) { + mc.flush(); + if (!avoidProtectedHack() || !func->isProtected()) { + QString virtualCall(methodCall); + QString normalCall(methodCall); + virtualCall = virtualCall.replace("%CLASS_NAME", func->ownerClass()->qualifiedCppName()); + normalCall = normalCall.replace("::%CLASS_NAME::", ""); + methodCall = ""; + mc << "Shiboken::Object::hasCppWrapper(reinterpret_cast(" PYTHON_SELF_VAR ")) ? "; + mc << virtualCall << " : " << normalCall; + } + } + } + } + + if (!injectedCodeCallsCppFunction(func)) { + s << INDENT << BEGIN_ALLOW_THREADS << endl << INDENT; + if (isCtor) { + s << (useVAddr.isEmpty() ? + QString("cptr = %1;").arg(methodCall) : useVAddr) << endl; + } else if (func->type() && !func->isInplaceOperator()) { + bool writeReturnType = true; + if (avoidProtectedHack()) { + const AbstractMetaEnum* metaEnum = findAbstractMetaEnum(func->type()); + if (metaEnum) { + QString enumName; + if (metaEnum->isProtected()) + enumName = protectedEnumSurrogateName(metaEnum); + else + enumName = func->type()->cppSignature(); + methodCall.prepend(enumName + '('); + methodCall.append(')'); + s << enumName; + writeReturnType = false; + } + } + if (writeReturnType) { + s << func->type()->cppSignature(); + if (isObjectTypeUsedAsValueType(func->type())) { + s << '*'; + methodCall.prepend(QString("new %1(").arg(func->type()->typeEntry()->qualifiedCppName())); + methodCall.append(')'); + } + } + s << " " CPP_RETURN_VAR " = "; + s << methodCall << ';' << endl; + } else { + s << methodCall << ';' << endl; + } + s << INDENT << END_ALLOW_THREADS << endl; + + if (!func->conversionRule(TypeSystem::TargetLangCode, 0).isEmpty()) { + writeConversionRule(s, func, TypeSystem::TargetLangCode, PYTHON_RETURN_VAR); + } else if (!isCtor && !func->isInplaceOperator() && func->type() + && !injectedCodeHasReturnValueAttribution(func, TypeSystem::TargetLangCode)) { + s << INDENT << PYTHON_RETURN_VAR " = "; + if (isObjectTypeUsedAsValueType(func->type())) { + s << "Shiboken::Object::newObject((SbkObjectType*)" << cpythonTypeNameExt(func->type()->typeEntry()); + s << ", " << CPP_RETURN_VAR << ", true, true)"; + } else { + writeToPythonConversion(s, func->type(), func->ownerClass(), CPP_RETURN_VAR); + } + s << ';' << endl; + } + } + } + + if (func->hasInjectedCode() && !func->isConstructor()) { + s << endl; + writeCodeSnips(s, snips, CodeSnip::End, TypeSystem::TargetLangCode, func, lastArg); + } + + bool hasReturnPolicy = false; + + // Ownership transference between C++ and Python. + QList ownership_mods; + // Python object reference management. + QList refcount_mods; + foreach (FunctionModification func_mod, func->modifications()) { + foreach (ArgumentModification arg_mod, func_mod.argument_mods) { + if (!arg_mod.ownerships.isEmpty() && arg_mod.ownerships.contains(TypeSystem::TargetLangCode)) + ownership_mods.append(arg_mod); + else if (!arg_mod.referenceCounts.isEmpty()) + refcount_mods.append(arg_mod); + } + } + + // If there's already a setParent(return, me), don't use the return heuristic! + if (func->argumentOwner(func->ownerClass(), -1).index == 0) + hasReturnPolicy = true; + + if (!ownership_mods.isEmpty()) { + s << endl << INDENT << "// Ownership transferences." << endl; + foreach (ArgumentModification arg_mod, ownership_mods) { + const AbstractMetaClass* wrappedClass = 0; + QString pyArgName = argumentNameFromIndex(func, arg_mod.index, &wrappedClass); + if (!wrappedClass) { + s << "#error Invalid ownership modification for argument " << arg_mod.index << '(' << pyArgName << ')' << endl << endl; + break; + } + + if (arg_mod.index == 0 || arg_mod.owner.index == 0) + hasReturnPolicy = true; + + // The default ownership does nothing. This is useful to avoid automatic heuristically + // based generation of code defining parenting. + if (arg_mod.ownerships[TypeSystem::TargetLangCode] == TypeSystem::DefaultOwnership) + continue; + + s << INDENT << "Shiboken::Object::"; + if (arg_mod.ownerships[TypeSystem::TargetLangCode] == TypeSystem::TargetLangOwnership) { + s << "getOwnership(" << pyArgName << ");"; + } else if (wrappedClass->hasVirtualDestructor()) { + if (arg_mod.index == 0) + s << "releaseOwnership(" PYTHON_RETURN_VAR ");"; + else + s << "releaseOwnership(" << pyArgName << ");"; + } else { + s << "invalidate(" << pyArgName << ");"; + } + s << endl; + } + + } else if (!refcount_mods.isEmpty()) { + foreach (ArgumentModification arg_mod, refcount_mods) { + ReferenceCount refCount = arg_mod.referenceCounts.first(); + if (refCount.action != ReferenceCount::Set + && refCount.action != ReferenceCount::Remove + && refCount.action != ReferenceCount::Add) { + ReportHandler::warning("\"set\", \"add\" and \"remove\" are the only values supported by Shiboken for action attribute of reference-count tag."); + continue; + } + const AbstractMetaClass* wrappedClass = 0; + + QString pyArgName; + if (refCount.action == ReferenceCount::Remove) { + pyArgName = "Py_None"; + } else { + pyArgName = argumentNameFromIndex(func, arg_mod.index, &wrappedClass); + if (pyArgName.isEmpty()) { + s << "#error Invalid reference count modification for argument " << arg_mod.index << endl << endl; + break; + } + } + + if (refCount.action == ReferenceCount::Add || refCount.action == ReferenceCount::Set) + s << INDENT << "Shiboken::Object::keepReference("; + else + s << INDENT << "Shiboken::Object::removeReference("; + + s << "reinterpret_cast(" PYTHON_SELF_VAR "), \""; + QString varName = arg_mod.referenceCounts.first().varName; + if (varName.isEmpty()) + varName = func->minimalSignature() + QString().number(arg_mod.index); + + s << varName << "\", " << pyArgName + << (refCount.action == ReferenceCount::Add ? ", true" : "") + << ");" << endl; + + if (arg_mod.index == 0) + hasReturnPolicy = true; + } + } + writeParentChildManagement(s, func, !hasReturnPolicy); +} + +QStringList CppGenerator::getAncestorMultipleInheritance(const AbstractMetaClass* metaClass) +{ + QStringList result; + AbstractMetaClassList baseClases = getBaseClasses(metaClass); + if (!baseClases.isEmpty()) { + foreach (const AbstractMetaClass* baseClass, baseClases) { + result.append(QString("((size_t) static_cast(class_ptr)) - base").arg(baseClass->qualifiedCppName())); + result.append(QString("((size_t) static_cast((%2*)((void*)class_ptr))) - base").arg(baseClass->qualifiedCppName()).arg(metaClass->qualifiedCppName())); + } + foreach (const AbstractMetaClass* baseClass, baseClases) + result.append(getAncestorMultipleInheritance(baseClass)); + } + return result; +} + +void CppGenerator::writeMultipleInheritanceInitializerFunction(QTextStream& s, const AbstractMetaClass* metaClass) +{ + QString className = metaClass->qualifiedCppName(); + QStringList ancestors = getAncestorMultipleInheritance(metaClass); + s << "static int mi_offsets[] = { "; + for (int i = 0; i < ancestors.size(); i++) + s << "-1, "; + s << "-1 };" << endl; + s << "int*" << endl; + s << multipleInheritanceInitializerFunctionName(metaClass) << "(const void* cptr)" << endl; + s << '{' << endl; + s << INDENT << "if (mi_offsets[0] == -1) {" << endl; + { + Indentation indent(INDENT); + s << INDENT << "std::set offsets;" << endl; + s << INDENT << "std::set::iterator it;" << endl; + s << INDENT << "const " << className << "* class_ptr = reinterpret_cast(cptr);" << endl; + s << INDENT << "size_t base = (size_t) class_ptr;" << endl; + + foreach (QString ancestor, ancestors) + s << INDENT << "offsets.insert(" << ancestor << ");" << endl; + + s << endl; + s << INDENT << "offsets.erase(0);" << endl; + s << endl; + + s << INDENT << "int i = 0;" << endl; + s << INDENT << "for (it = offsets.begin(); it != offsets.end(); it++) {" << endl; + { + Indentation indent(INDENT); + s << INDENT << "mi_offsets[i] = *it;" << endl; + s << INDENT << "i++;" << endl; + } + s << INDENT << '}' << endl; + } + s << INDENT << '}' << endl; + s << INDENT << "return mi_offsets;" << endl; + s << '}' << endl; +} + +void CppGenerator::writeSpecialCastFunction(QTextStream& s, const AbstractMetaClass* metaClass) +{ + QString className = metaClass->qualifiedCppName(); + s << "static void* " << cpythonSpecialCastFunctionName(metaClass) << "(void* obj, SbkObjectType* desiredType)\n"; + s << "{\n"; + s << INDENT << className << "* me = reinterpret_cast< ::" << className << "*>(obj);\n"; + bool firstClass = true; + foreach (const AbstractMetaClass* baseClass, getAllAncestors(metaClass)) { + s << INDENT << (!firstClass ? "else " : "") << "if (desiredType == reinterpret_cast(" << cpythonTypeNameExt(baseClass->typeEntry()) << "))\n"; + Indentation indent(INDENT); + s << INDENT << "return static_cast< ::" << baseClass->qualifiedCppName() << "*>(me);\n"; + firstClass = false; + } + s << INDENT << "return me;\n"; + s << "}\n\n"; +} + +void CppGenerator::writePrimitiveConverterInitialization(QTextStream& s, const CustomConversion* customConversion) +{ + const TypeEntry* type = customConversion->ownerType(); + QString converter = converterObject(type); + s << INDENT << "// Register converter for type '" << type->qualifiedTargetLangName() << "'." << endl; + s << INDENT << converter << " = Shiboken::Conversions::createConverter("; + if (type->targetLangApiName() == type->name()) + s << '0'; + else if (type->targetLangApiName() == "PyObject") + s << "&PyBaseObject_Type"; + else + s << '&' << type->targetLangApiName() << "_Type"; + QString typeName = fixedCppTypeName(type); + s << ", " << cppToPythonFunctionName(typeName, typeName) << ");" << endl; + s << INDENT << "Shiboken::Conversions::registerConverterName(" << converter << ", \"" << type->qualifiedCppName() << "\");" << endl; + writeCustomConverterRegister(s, customConversion, converter); +} + +void CppGenerator::writeEnumConverterInitialization(QTextStream& s, const AbstractMetaEnum* metaEnum) +{ + if (metaEnum->isPrivate() || metaEnum->isAnonymous()) + return; + writeEnumConverterInitialization(s, metaEnum->typeEntry()); +} + +void CppGenerator::writeEnumConverterInitialization(QTextStream& s, const TypeEntry* enumType) +{ + if (!enumType) + return; + QString enumFlagName = enumType->isFlags() ? "flag" : "enum"; + QString enumPythonType = cpythonTypeNameExt(enumType); + + const FlagsTypeEntry* flags = 0; + if (enumType->isFlags()) + flags = reinterpret_cast(enumType); + + s << INDENT << "// Register converter for " << enumFlagName << " '" << enumType->qualifiedCppName() << "'." << endl; + s << INDENT << '{' << endl; + { + Indentation indent(INDENT); + QString typeName = fixedCppTypeName(enumType); + s << INDENT << "SbkConverter* converter = Shiboken::Conversions::createConverter(" << enumPythonType << ',' << endl; + { + Indentation indent(INDENT); + s << INDENT << cppToPythonFunctionName(typeName, typeName) << ");" << endl; + } + + if (flags) { + QString enumTypeName = fixedCppTypeName(flags->originator()); + QString toCpp = pythonToCppFunctionName(enumTypeName, typeName); + QString isConv = convertibleToCppFunctionName(enumTypeName, typeName); + writeAddPythonToCppConversion(s, "converter", toCpp, isConv); + } + + QString toCpp = pythonToCppFunctionName(typeName, typeName); + QString isConv = convertibleToCppFunctionName(typeName, typeName); + writeAddPythonToCppConversion(s, "converter", toCpp, isConv); + + if (flags) { + QString toCpp = pythonToCppFunctionName("number", typeName); + QString isConv = convertibleToCppFunctionName("number", typeName); + writeAddPythonToCppConversion(s, "converter", toCpp, isConv); + } + + s << INDENT << "Shiboken::Enum::setTypeConverter(" << enumPythonType << ", converter);" << endl; + s << INDENT << "Shiboken::Enum::setTypeConverter(" << enumPythonType << ", converter);" << endl; + QStringList cppSignature = enumType->qualifiedCppName().split("::", QString::SkipEmptyParts); + while (!cppSignature.isEmpty()) { + QString signature = cppSignature.join("::"); + s << INDENT << "Shiboken::Conversions::registerConverterName(converter, \""; + if (flags) + s << "QFlags<"; + s << signature << "\");" << endl; + cppSignature.removeFirst(); + } + } + s << INDENT << '}' << endl; + + if (!flags) + writeEnumConverterInitialization(s, reinterpret_cast(enumType)->flags()); +} + +void CppGenerator::writeContainerConverterInitialization(QTextStream& s, const AbstractMetaType* type) +{ + QByteArray cppSignature = QMetaObject::normalizedSignature(type->cppSignature().toAscii()); + s << INDENT << "// Register converter for type '" << cppSignature << "'." << endl; + QString converter = converterObject(type); + s << INDENT << converter << " = Shiboken::Conversions::createConverter("; + if (type->typeEntry()->targetLangApiName() == "PyObject") { + s << "&PyBaseObject_Type"; + } else { + QString baseName = cpythonBaseName(type->typeEntry()); + if (baseName == "PySequence") + baseName = "PyList"; + s << '&' << baseName << "_Type"; + } + QString typeName = fixedCppTypeName(type); + s << ", " << cppToPythonFunctionName(typeName, typeName) << ");" << endl; + QString toCpp = pythonToCppFunctionName(typeName, typeName); + QString isConv = convertibleToCppFunctionName(typeName, typeName); + s << INDENT << "Shiboken::Conversions::registerConverterName(" << converter << ", \"" << cppSignature << "\");" << endl; + if (usePySideExtensions() && cppSignature.startsWith("const ") && cppSignature.endsWith("&")) { + cppSignature.chop(1); + cppSignature.remove(0, sizeof("const ") / sizeof(char) - 1); + s << INDENT << "Shiboken::Conversions::registerConverterName(" << converter << ", \"" << cppSignature << "\");" << endl; + } + writeAddPythonToCppConversion(s, converterObject(type), toCpp, isConv); +} + +void CppGenerator::writeExtendedConverterInitialization(QTextStream& s, const TypeEntry* externalType, const QList& conversions) +{ + s << INDENT << "// Extended implicit conversions for " << externalType->qualifiedTargetLangName() << '.' << endl; + foreach (const AbstractMetaClass* sourceClass, conversions) { + QString converterVar = QString("(SbkObjectType*)%1[%2]") + .arg(cppApiVariableName(externalType->targetLangPackage())) + .arg(getTypeIndexVariableName(externalType)); + QString sourceTypeName = fixedCppTypeName(sourceClass->typeEntry()); + QString targetTypeName = fixedCppTypeName(externalType); + QString toCpp = pythonToCppFunctionName(sourceTypeName, targetTypeName); + QString isConv = convertibleToCppFunctionName(sourceTypeName, targetTypeName); + writeAddPythonToCppConversion(s, converterVar, toCpp, isConv); + } +} + +QString CppGenerator::multipleInheritanceInitializerFunctionName(const AbstractMetaClass* metaClass) +{ + if (!hasMultipleInheritanceInAncestry(metaClass)) + return QString(); + return QString("%1_mi_init").arg(cpythonBaseName(metaClass->typeEntry())); +} + +bool CppGenerator::supportsMappingProtocol(const AbstractMetaClass* metaClass) +{ + foreach(QString funcName, m_mappingProtocol.keys()) { + if (metaClass->hasFunction(funcName)) + return true; + } + + return false; +} + +bool CppGenerator::supportsNumberProtocol(const AbstractMetaClass* metaClass) +{ + return metaClass->hasArithmeticOperatorOverload() + || metaClass->hasLogicalOperatorOverload() + || metaClass->hasBitwiseOperatorOverload() + || hasBoolCast(metaClass); +} + +bool CppGenerator::supportsSequenceProtocol(const AbstractMetaClass* metaClass) +{ + foreach(QString funcName, m_sequenceProtocol.keys()) { + if (metaClass->hasFunction(funcName)) + return true; + } + + const ComplexTypeEntry* baseType = metaClass->typeEntry()->baseContainerType(); + if (baseType && baseType->isContainer()) + return true; + + return false; +} + +bool CppGenerator::shouldGenerateGetSetList(const AbstractMetaClass* metaClass) +{ + foreach (AbstractMetaField* f, metaClass->fields()) { + if (!f->isStatic()) + return true; + } + return false; +} + +void CppGenerator::writeClassDefinition(QTextStream& s, const AbstractMetaClass* metaClass) +{ + QString tp_flags; + QString tp_init; + QString tp_new; + QString tp_dealloc; + QString tp_hash('0'); + QString tp_call('0'); + QString cppClassName = metaClass->qualifiedCppName(); + QString className = cpythonTypeName(metaClass).replace(QRegExp("_Type$"), ""); + QString baseClassName('0'); + AbstractMetaFunctionList ctors; + foreach (AbstractMetaFunction* f, metaClass->queryFunctions(AbstractMetaClass::Constructors)) { + if (!f->isPrivate() && !f->isModifiedRemoved()) + ctors.append(f); + } + + if (!metaClass->baseClass()) + baseClassName = "reinterpret_cast(&SbkObject_Type)"; + + bool onlyPrivCtor = !metaClass->hasNonPrivateConstructor(); + + if (metaClass->isNamespace() || metaClass->hasPrivateDestructor()) { + tp_flags = "Py_TPFLAGS_DEFAULT|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_GC"; + tp_dealloc = metaClass->hasPrivateDestructor() ? + "SbkDeallocWrapperWithPrivateDtor" : "0"; + tp_init = "0"; + } else { + if (onlyPrivCtor) + tp_flags = "Py_TPFLAGS_DEFAULT|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_GC"; + else + tp_flags = "Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_GC"; + + QString deallocClassName; + if (shouldGenerateCppWrapper(metaClass)) + deallocClassName = wrapperName(metaClass); + else + deallocClassName = cppClassName; + tp_dealloc = "&SbkDeallocWrapper"; + tp_init = onlyPrivCtor || ctors.isEmpty() ? "0" : cpythonFunctionName(ctors.first()); + } + + QString tp_getattro('0'); + QString tp_setattro('0'); + if (usePySideExtensions() && (metaClass->qualifiedCppName() == "QObject")) { + tp_getattro = cpythonGetattroFunctionName(metaClass); + tp_setattro = cpythonSetattroFunctionName(metaClass); + } else if (classNeedsGetattroFunction(metaClass)) { + tp_getattro = cpythonGetattroFunctionName(metaClass); + } + + if (metaClass->hasPrivateDestructor() || onlyPrivCtor) + tp_new = "0"; + else + tp_new = "SbkObjectTpNew"; + + QString tp_richcompare = QString('0'); + if (metaClass->hasComparisonOperatorOverload()) + tp_richcompare = cpythonBaseName(metaClass) + "_richcompare"; + + QString tp_getset = QString('0'); + if (shouldGenerateGetSetList(metaClass)) + tp_getset = cpythonGettersSettersDefinitionName(metaClass); + + // search for special functions + ShibokenGenerator::clearTpFuncs(); + foreach (AbstractMetaFunction* func, metaClass->functions()) { + if (m_tpFuncs.contains(func->name())) + m_tpFuncs[func->name()] = cpythonFunctionName(func); + } + if (m_tpFuncs["__repr__"] == "0" + && !metaClass->isQObject() + && metaClass->hasToStringCapability()) { + m_tpFuncs["__repr__"] = writeReprFunction(s, metaClass); + } + + // class or some ancestor has multiple inheritance + const AbstractMetaClass* miClass = getMultipleInheritingClass(metaClass); + if (miClass) { + if (metaClass == miClass) + writeMultipleInheritanceInitializerFunction(s, metaClass); + writeSpecialCastFunction(s, metaClass); + s << endl; + } + + if (!metaClass->typeEntry()->hashFunction().isEmpty()) + tp_hash = '&' + cpythonBaseName(metaClass) + "_HashFunc"; + + const AbstractMetaFunction* callOp = metaClass->findFunction("operator()"); + if (callOp && !callOp->isModifiedRemoved()) + tp_call = '&' + cpythonFunctionName(callOp); + + + s << "// Class Definition -----------------------------------------------" << endl; + s << "extern \"C\" {" << endl; + s << "static SbkObjectType " << className + "_Type" << " = { { {" << endl; + s << INDENT << "PyVarObject_HEAD_INIT(&SbkObjectType_Type, 0)" << endl; + s << INDENT << "/*tp_name*/ \"" << getClassTargetFullName(metaClass) << "\"," << endl; + s << INDENT << "/*tp_basicsize*/ sizeof(SbkObject)," << endl; + s << INDENT << "/*tp_itemsize*/ 0," << endl; + s << INDENT << "/*tp_dealloc*/ " << tp_dealloc << ',' << endl; + s << INDENT << "/*tp_print*/ 0," << endl; + s << INDENT << "/*tp_getattr*/ 0," << endl; + s << INDENT << "/*tp_setattr*/ 0," << endl; + s << INDENT << "/*tp_compare*/ 0," << endl; + s << INDENT << "/*tp_repr*/ " << m_tpFuncs["__repr__"] << "," << endl; + s << INDENT << "/*tp_as_number*/ 0," << endl; + s << INDENT << "/*tp_as_sequence*/ 0," << endl; + s << INDENT << "/*tp_as_mapping*/ 0," << endl; + s << INDENT << "/*tp_hash*/ " << tp_hash << ',' << endl; + s << INDENT << "/*tp_call*/ " << tp_call << ',' << endl; + s << INDENT << "/*tp_str*/ " << m_tpFuncs["__str__"] << ',' << endl; + s << INDENT << "/*tp_getattro*/ " << tp_getattro << ',' << endl; + s << INDENT << "/*tp_setattro*/ " << tp_setattro << ',' << endl; + s << INDENT << "/*tp_as_buffer*/ 0," << endl; + s << INDENT << "/*tp_flags*/ " << tp_flags << ',' << endl; + s << INDENT << "/*tp_doc*/ 0," << endl; + s << INDENT << "/*tp_traverse*/ " << className << "_traverse," << endl; + s << INDENT << "/*tp_clear*/ " << className << "_clear," << endl; + s << INDENT << "/*tp_richcompare*/ " << tp_richcompare << ',' << endl; + s << INDENT << "/*tp_weaklistoffset*/ 0," << endl; + s << INDENT << "/*tp_iter*/ " << m_tpFuncs["__iter__"] << ',' << endl; + s << INDENT << "/*tp_iternext*/ " << m_tpFuncs["__next__"] << ',' << endl; + s << INDENT << "/*tp_methods*/ " << className << "_methods," << endl; + s << INDENT << "/*tp_members*/ 0," << endl; + s << INDENT << "/*tp_getset*/ " << tp_getset << ',' << endl; + s << INDENT << "/*tp_base*/ " << baseClassName << ',' << endl; + s << INDENT << "/*tp_dict*/ 0," << endl; + s << INDENT << "/*tp_descr_get*/ 0," << endl; + s << INDENT << "/*tp_descr_set*/ 0," << endl; + s << INDENT << "/*tp_dictoffset*/ 0," << endl; + s << INDENT << "/*tp_init*/ " << tp_init << ',' << endl; + s << INDENT << "/*tp_alloc*/ 0," << endl; + s << INDENT << "/*tp_new*/ " << tp_new << ',' << endl; + s << INDENT << "/*tp_free*/ 0," << endl; + s << INDENT << "/*tp_is_gc*/ 0," << endl; + s << INDENT << "/*tp_bases*/ 0," << endl; + s << INDENT << "/*tp_mro*/ 0," << endl; + s << INDENT << "/*tp_cache*/ 0," << endl; + s << INDENT << "/*tp_subclasses*/ 0," << endl; + s << INDENT << "/*tp_weaklist*/ 0" << endl; + s << "}, }," << endl; + s << INDENT << "/*priv_data*/ 0" << endl; + s << "};" << endl; + QString suffix; + if (isObjectType(metaClass)) + suffix = "*"; + s << "} //extern" << endl; +} + +void CppGenerator::writeMappingMethods(QTextStream& s, const AbstractMetaClass* metaClass) +{ + + QMap funcs; + + QHash< QString, QPair< QString, QString > >::const_iterator it = m_mappingProtocol.begin(); + for (; it != m_mappingProtocol.end(); ++it) { + const AbstractMetaFunction* func = metaClass->findFunction(it.key()); + if (!func) + continue; + QString funcName = cpythonFunctionName(func); + QString funcArgs = it.value().first; + QString funcRetVal = it.value().second; + + CodeSnipList snips = func->injectedCodeSnips(CodeSnip::Any, TypeSystem::TargetLangCode); + s << funcRetVal << ' ' << funcName << '(' << funcArgs << ')' << endl << '{' << endl; + writeInvalidPyObjectCheck(s, PYTHON_SELF_VAR); + + writeCppSelfDefinition(s, func); + + const AbstractMetaArgument* lastArg = func->arguments().isEmpty() ? 0 : func->arguments().last(); + writeCodeSnips(s, snips, CodeSnip::Any, TypeSystem::TargetLangCode, func, lastArg); + s << '}' << endl << endl; + } +} + +void CppGenerator::writeSequenceMethods(QTextStream& s, const AbstractMetaClass* metaClass) +{ + + QMap funcs; + bool injectedCode = false; + + QHash< QString, QPair< QString, QString > >::const_iterator it = m_sequenceProtocol.begin(); + for (; it != m_sequenceProtocol.end(); ++it) { + const AbstractMetaFunction* func = metaClass->findFunction(it.key()); + if (!func) + continue; + injectedCode = true; + QString funcName = cpythonFunctionName(func); + QString funcArgs = it.value().first; + QString funcRetVal = it.value().second; + + CodeSnipList snips = func->injectedCodeSnips(CodeSnip::Any, TypeSystem::TargetLangCode); + s << funcRetVal << ' ' << funcName << '(' << funcArgs << ')' << endl << '{' << endl; + writeInvalidPyObjectCheck(s, PYTHON_SELF_VAR); + + writeCppSelfDefinition(s, func); + + const AbstractMetaArgument* lastArg = func->arguments().isEmpty() ? 0 : func->arguments().last(); + writeCodeSnips(s, snips,CodeSnip::Any, TypeSystem::TargetLangCode, func, lastArg); + s << '}' << endl << endl; + } + + if (!injectedCode) + writeStdListWrapperMethods(s, metaClass); +} + +void CppGenerator::writeTypeAsSequenceDefinition(QTextStream& s, const AbstractMetaClass* metaClass) +{ + bool hasFunctions = false; + QMap funcs; + foreach(QString funcName, m_sequenceProtocol.keys()) { + const AbstractMetaFunction* func = metaClass->findFunction(funcName); + funcs[funcName] = func ? cpythonFunctionName(func).prepend("&") : QString(); + if (!hasFunctions && func) + hasFunctions = true; + } + + QString baseName = cpythonBaseName(metaClass); + + //use default implementation + if (!hasFunctions) { + funcs["__len__"] = baseName + "__len__"; + funcs["__getitem__"] = baseName + "__getitem__"; + funcs["__setitem__"] = baseName + "__setitem__"; + } + + s << INDENT << "memset(&" << baseName << "_Type.super.as_sequence, 0, sizeof(PySequenceMethods));" << endl; + foreach (const QString& sqName, m_sqFuncs.keys()) { + if (funcs[sqName].isEmpty()) + continue; + if (m_sqFuncs[sqName] == "sq_slice") + s << "#ifndef IS_PY3K" << endl; + s << INDENT << baseName << "_Type.super.as_sequence." << m_sqFuncs[sqName] << " = " << funcs[sqName] << ';' << endl; + if (m_sqFuncs[sqName] == "sq_slice") + s << "#endif" << endl; + } +} + +void CppGenerator::writeTypeAsMappingDefinition(QTextStream& s, const AbstractMetaClass* metaClass) +{ + bool hasFunctions = false; + QMap funcs; + foreach(QString funcName, m_mappingProtocol.keys()) { + const AbstractMetaFunction* func = metaClass->findFunction(funcName); + funcs[funcName] = func ? cpythonFunctionName(func).prepend("&") : "0"; + if (!hasFunctions && func) + hasFunctions = true; + } + + //use default implementation + if (!hasFunctions) { + funcs["__mlen__"] = QString(); + funcs["__mgetitem__"] = QString(); + funcs["__msetitem__"] = QString(); + } + + QString baseName = cpythonBaseName(metaClass); + s << INDENT << "memset(&" << baseName << "_Type.super.as_mapping, 0, sizeof(PyMappingMethods));" << endl; + foreach (const QString& mpName, m_mpFuncs.keys()) { + if (funcs[mpName].isEmpty()) + continue; + s << INDENT << baseName << "_Type.super.as_mapping." << m_mpFuncs[mpName] << " = " << funcs[mpName] << ';' << endl; + } +} + +void CppGenerator::writeTypeAsNumberDefinition(QTextStream& s, const AbstractMetaClass* metaClass) +{ + QMap nb; + + nb["__add__"] = QString(); + nb["__sub__"] = QString(); + nb["__mul__"] = QString(); + nb["__div__"] = QString(); + nb["__mod__"] = QString(); + nb["__neg__"] = QString(); + nb["__pos__"] = QString(); + nb["__invert__"] = QString(); + nb["__lshift__"] = QString(); + nb["__rshift__"] = QString(); + nb["__and__"] = QString(); + nb["__xor__"] = QString(); + nb["__or__"] = QString(); + nb["__iadd__"] = QString(); + nb["__isub__"] = QString(); + nb["__imul__"] = QString(); + nb["__idiv__"] = QString(); + nb["__imod__"] = QString(); + nb["__ilshift__"] = QString(); + nb["__irshift__"] = QString(); + nb["__iand__"] = QString(); + nb["__ixor__"] = QString(); + nb["__ior__"] = QString(); + + QList opOverloads = + filterGroupedOperatorFunctions(metaClass, + AbstractMetaClass::ArithmeticOp + | AbstractMetaClass::LogicalOp + | AbstractMetaClass::BitwiseOp); + + foreach (AbstractMetaFunctionList opOverload, opOverloads) { + const AbstractMetaFunction* rfunc = opOverload[0]; + QString opName = ShibokenGenerator::pythonOperatorFunctionName(rfunc); + nb[opName] = cpythonFunctionName(rfunc); + } + + QString baseName = cpythonBaseName(metaClass); + + nb["bool"] = hasBoolCast(metaClass) ? baseName + "___nb_bool" : QString(); + + s << INDENT << "memset(&" << baseName << "_Type.super.as_number, 0, sizeof(PyNumberMethods));" << endl; + foreach (const QString& nbName, m_nbFuncs.keys()) { + if (nb[nbName].isEmpty()) + continue; + + // bool is special because the field name differs on Python 2 and 3 (nb_nonzero vs nb_bool) + // so a shiboken macro is used. + if (nbName == "bool") { + s << INDENT << "SBK_NB_BOOL(" << baseName << "_Type.super.as_number) = " << nb[nbName] << ';' << endl; + } else { + bool excludeFromPy3K = nbName == "__div__" || nbName == "__idiv__"; + if (excludeFromPy3K) + s << "#ifndef IS_PY3K" << endl; + s << INDENT << baseName << "_Type.super.as_number." << m_nbFuncs[nbName] << " = " << nb[nbName] << ';' << endl; + if (excludeFromPy3K) + s << "#endif" << endl; + } + } + if (!nb["__div__"].isEmpty()) + s << INDENT << baseName << "_Type.super.as_number.nb_true_divide = " << nb["__div__"] << ';' << endl; +} + +void CppGenerator::writeTpTraverseFunction(QTextStream& s, const AbstractMetaClass* metaClass) +{ + QString baseName = cpythonBaseName(metaClass); + s << "static int "; + s << baseName << "_traverse(PyObject* " PYTHON_SELF_VAR ", visitproc visit, void* arg)" << endl; + s << '{' << endl; + s << INDENT << "return reinterpret_cast(&SbkObject_Type)->tp_traverse(" PYTHON_SELF_VAR ", visit, arg);" << endl; + s << '}' << endl; +} + +void CppGenerator::writeTpClearFunction(QTextStream& s, const AbstractMetaClass* metaClass) +{ + QString baseName = cpythonBaseName(metaClass); + s << "static int "; + s << baseName << "_clear(PyObject* " PYTHON_SELF_VAR ")" << endl; + s << '{' << endl; + s << INDENT << "return reinterpret_cast(&SbkObject_Type)->tp_clear(" PYTHON_SELF_VAR ");" << endl; + s << '}' << endl; +} + +void CppGenerator::writeCopyFunction(QTextStream& s, const AbstractMetaClass* metaClass) +{ + QString className = cpythonTypeName(metaClass).replace(QRegExp("_Type$"), ""); + s << "static PyObject* " << className << "___copy__(PyObject* " PYTHON_SELF_VAR ")" << endl; + s << "{" << endl; + writeCppSelfDefinition(s, metaClass, false, true); + s << INDENT << "PyObject* " << PYTHON_RETURN_VAR << " = " << cpythonToPythonConversionFunction(metaClass); + s << CPP_SELF_VAR ");" << endl; + writeFunctionReturnErrorCheckSection(s); + s << INDENT << "return " PYTHON_RETURN_VAR ";" << endl; + s << "}" << endl; + s << endl; +} + +void CppGenerator::writeGetterFunction(QTextStream& s, const AbstractMetaField* metaField) +{ + ErrorCode errorCode(0); + s << "static PyObject* " << cpythonGetterFunctionName(metaField) << "(PyObject* " PYTHON_SELF_VAR ", void*)" << endl; + s << '{' << endl; + + writeCppSelfDefinition(s, metaField->enclosingClass()); + + AbstractMetaType* fieldType = metaField->type(); + // Force use of pointer to return internal variable memory + bool newWrapperSameObject = !fieldType->isConstant() && isWrapperType(fieldType) && !isPointer(fieldType); + + QString cppField; + if (avoidProtectedHack() && metaField->isProtected()) { + cppField = QString("((%1*)%2)->%3()").arg(wrapperName(metaField->enclosingClass())) + .arg(CPP_SELF_VAR) + .arg(protectedFieldGetterName(metaField)); + } else { + cppField = QString("%2->%3").arg(CPP_SELF_VAR).arg(metaField->name()); + if (newWrapperSameObject) { + cppField.prepend("&("); + cppField.append(')'); + } + } + if (isCppIntegralPrimitive(fieldType) || fieldType->isEnum()) { + s << INDENT << getFullTypeNameWithoutModifiers(fieldType) << " cppOut_local = " << cppField << ';' << endl; + cppField = "cppOut_local"; + } else if (avoidProtectedHack() && metaField->isProtected()) { + s << INDENT << getFullTypeNameWithoutModifiers(fieldType); + if (fieldType->isContainer() || fieldType->isFlags()) { + s << '&'; + cppField.prepend('*'); + } else if ((!fieldType->isConstant() && !fieldType->isEnum() && !fieldType->isPrimitive()) || fieldType->indirections() == 1) { + s << '*'; + } + s << " fieldValue = " << cppField << ';' << endl; + cppField = "fieldValue"; + } + + s << INDENT << "PyObject* pyOut = "; + if (newWrapperSameObject) { + s << "Shiboken::Object::newObject((SbkObjectType*)" << cpythonTypeNameExt(fieldType); + s << ", " << cppField << ", false, true);" << endl; + s << INDENT << "Shiboken::Object::setParent(" PYTHON_SELF_VAR ", pyOut)"; + } else { + writeToPythonConversion(s, fieldType, metaField->enclosingClass(), cppField); + } + s << ';' << endl; + + s << INDENT << "return pyOut;" << endl; + s << '}' << endl; +} + +void CppGenerator::writeSetterFunction(QTextStream& s, const AbstractMetaField* metaField) +{ + ErrorCode errorCode(0); + s << "static int " << cpythonSetterFunctionName(metaField) << "(PyObject* " PYTHON_SELF_VAR ", PyObject* pyIn, void*)" << endl; + s << '{' << endl; + + writeCppSelfDefinition(s, metaField->enclosingClass()); + + s << INDENT << "if (pyIn == 0) {" << endl; + { + Indentation indent(INDENT); + s << INDENT << "PyErr_SetString(PyExc_TypeError, \"'"; + s << metaField->name() << "' may not be deleted\");" << endl; + s << INDENT << "return -1;" << endl; + } + s << INDENT << '}' << endl; + + AbstractMetaType* fieldType = metaField->type(); + + s << INDENT << "PythonToCppFunc " << PYTHON_TO_CPP_VAR << ';' << endl; + s << INDENT << "if (!"; + writeTypeCheck(s, fieldType, "pyIn", isNumber(fieldType->typeEntry())); + s << ") {" << endl; + { + Indentation indent(INDENT); + s << INDENT << "PyErr_SetString(PyExc_TypeError, \"wrong type attributed to '"; + s << metaField->name() << "', '" << fieldType->name() << "' or convertible type expected\");" << endl; + s << INDENT << "return -1;" << endl; + } + s << INDENT << '}' << endl << endl; + + QString cppField = QString("%1->%2").arg(CPP_SELF_VAR).arg(metaField->name()); + s << INDENT; + if (avoidProtectedHack() && metaField->isProtected()) { + s << getFullTypeNameWithoutModifiers(fieldType); + s << (fieldType->indirections() == 1 ? "*" : "") << " cppOut;" << endl; + s << INDENT << PYTHON_TO_CPP_VAR << "(pyIn, &cppOut);" << endl; + s << INDENT << QString("((%1*)%2)->%3(cppOut)").arg(wrapperName(metaField->enclosingClass())) + .arg(CPP_SELF_VAR) + .arg(protectedFieldSetterName(metaField)); + } else if (isCppIntegralPrimitive(fieldType) || fieldType->typeEntry()->isEnum() || fieldType->typeEntry()->isFlags()) { + s << getFullTypeNameWithoutModifiers(fieldType) << " cppOut_local = " << cppField << ';' << endl; + s << INDENT << PYTHON_TO_CPP_VAR << "(pyIn, &cppOut_local);" << endl; + s << INDENT << cppField << " = cppOut_local"; + } else { + s << getFullTypeNameWithoutModifiers(fieldType); + s << QString("*").repeated(fieldType->indirections()) << "& cppOut_ptr = "; + s << cppField << ';' << endl; + s << INDENT << PYTHON_TO_CPP_VAR << "(pyIn, &cppOut_ptr)"; + } + s << ';' << endl << endl; + + if (isPointerToWrapperType(fieldType)) { + s << INDENT << "Shiboken::Object::keepReference(reinterpret_cast(" PYTHON_SELF_VAR "), \""; + s << metaField->name() << "\", pyIn);" << endl; + } + + s << INDENT << "return 0;" << endl; + s << '}' << endl; +} + +void CppGenerator::writeRichCompareFunction(QTextStream& s, const AbstractMetaClass* metaClass) +{ + QString baseName = cpythonBaseName(metaClass); + s << "static PyObject* "; + s << baseName << "_richcompare(PyObject* " PYTHON_SELF_VAR ", PyObject* " PYTHON_ARG ", int op)" << endl; + s << '{' << endl; + writeCppSelfDefinition(s, metaClass, false, true); + writeUnusedVariableCast(s, CPP_SELF_VAR); + s << INDENT << "PyObject* " PYTHON_RETURN_VAR " = 0;" << endl; + s << INDENT << "PythonToCppFunc " PYTHON_TO_CPP_VAR << ';' << endl; + writeUnusedVariableCast(s, PYTHON_TO_CPP_VAR); + s << endl; + + s << INDENT << "switch (op) {" << endl; + { + Indentation indent(INDENT); + foreach (AbstractMetaFunctionList overloads, filterGroupedOperatorFunctions(metaClass, AbstractMetaClass::ComparisonOp)) { + const AbstractMetaFunction* rfunc = overloads[0]; + + QString operatorId = ShibokenGenerator::pythonRichCompareOperatorId(rfunc); + s << INDENT << "case " << operatorId << ':' << endl; + + Indentation indent(INDENT); + + QString op = rfunc->originalName(); + op = op.right(op.size() - QString("operator").size()); + + int alternativeNumericTypes = 0; + foreach (const AbstractMetaFunction* func, overloads) { + if (!func->isStatic() && + ShibokenGenerator::isNumber(func->arguments()[0]->type()->typeEntry())) + alternativeNumericTypes++; + } + + bool first = true; + OverloadData overloadData(overloads, this); + foreach (OverloadData* od, overloadData.nextOverloadData()) { + const AbstractMetaFunction* func = od->referenceFunction(); + if (func->isStatic()) + continue; + const AbstractMetaType* argType = getArgumentType(func, 1); + if (!argType) + continue; + if (!first) { + s << " else "; + } else { + first = false; + s << INDENT; + } + s << "if ("; + writeTypeCheck(s, argType, PYTHON_ARG, alternativeNumericTypes == 1 || isPyInt(argType)); + s << ") {" << endl; + { + Indentation indent(INDENT); + s << INDENT << "// " << func->signature() << endl; + writeArgumentConversion(s, argType, CPP_ARG0, PYTHON_ARG, metaClass, QString(), func->isUserAdded()); + + // If the function is user added, use the inject code + if (func->isUserAdded()) { + CodeSnipList snips = func->injectedCodeSnips(); + writeCodeSnips(s, snips, CodeSnip::Any, TypeSystem::TargetLangCode, func, func->arguments().last()); + } else { + QString expression = QString("%1%2 %3 (%4" CPP_ARG0 ")") + .arg(func->isPointerOperator() ? "&" : "") + .arg(CPP_SELF_VAR).arg(op) + .arg(shouldDereferenceAbstractMetaTypePointer(argType) ? "*" : ""); + s << INDENT; + if (func->type()) + s << func->type()->cppSignature() << " " CPP_RETURN_VAR " = "; + s << expression << ';' << endl; + s << INDENT << PYTHON_RETURN_VAR " = "; + if (func->type()) + writeToPythonConversion(s, func->type(), metaClass, CPP_RETURN_VAR); + else + s << "Py_None;" << endl << INDENT << "Py_INCREF(Py_None)"; + s << ';' << endl; + } + } + s << INDENT << '}'; + } + + s << " else {" << endl; + if (operatorId == "Py_EQ" || operatorId == "Py_NE") { + Indentation indent(INDENT); + s << INDENT << PYTHON_RETURN_VAR " = " << (operatorId == "Py_EQ" ? "Py_False" : "Py_True") << ';' << endl; + s << INDENT << "Py_INCREF(" PYTHON_RETURN_VAR ");" << endl; + } else { + Indentation indent(INDENT); + s << INDENT << "goto " << baseName << "_RichComparison_TypeError;" << endl; + } + s << INDENT << '}' << endl << endl; + + s << INDENT << "break;" << endl; + } + s << INDENT << "default:" << endl; + { + Indentation indent(INDENT); + s << INDENT << "goto " << baseName << "_RichComparison_TypeError;" << endl; + } + } + s << INDENT << '}' << endl << endl; + + s << INDENT << "if (" PYTHON_RETURN_VAR " && !PyErr_Occurred())" << endl; + { + Indentation indent(INDENT); + s << INDENT << "return " PYTHON_RETURN_VAR ";" << endl; + } + s << INDENT << baseName << "_RichComparison_TypeError:" << endl; + s << INDENT << "PyErr_SetString(PyExc_NotImplementedError, \"operator not implemented.\");" << endl; + s << INDENT << "return " << m_currentErrorCode << ';' << endl << endl; + s << '}' << endl << endl; +} + +void CppGenerator::writeMethodDefinitionEntry(QTextStream& s, const AbstractMetaFunctionList overloads) +{ + Q_ASSERT(!overloads.isEmpty()); + OverloadData overloadData(overloads, this); + bool usePyArgs = pythonFunctionWrapperUsesListOfArguments(overloadData); + const AbstractMetaFunction* func = overloadData.referenceFunction(); + int min = overloadData.minArgs(); + int max = overloadData.maxArgs(); + + s << '"' << func->name() << "\", (PyCFunction)" << cpythonFunctionName(func) << ", "; + if ((min == max) && (max < 2) && !usePyArgs) { + if (max == 0) + s << "METH_NOARGS"; + else + s << "METH_O"; + } else { + s << "METH_VARARGS"; + if (overloadData.hasArgumentWithDefaultValue()) + s << "|METH_KEYWORDS"; + } + if (func->ownerClass() && overloadData.hasStaticFunction()) + s << "|METH_STATIC"; +} + +void CppGenerator::writeMethodDefinition(QTextStream& s, const AbstractMetaFunctionList overloads) +{ + Q_ASSERT(!overloads.isEmpty()); + const AbstractMetaFunction* func = overloads.first(); + if (m_tpFuncs.contains(func->name())) + return; + + s << INDENT; + if (OverloadData::hasStaticAndInstanceFunctions(overloads)) { + s << cpythonMethodDefinitionName(func); + } else { + s << '{'; + writeMethodDefinitionEntry(s, overloads); + s << '}'; + } + s << ',' << endl; +} + +void CppGenerator::writeEnumsInitialization(QTextStream& s, AbstractMetaEnumList& enums) +{ + if (enums.isEmpty()) + return; + s << INDENT << "// Initialization of enums." << endl << endl; + foreach (const AbstractMetaEnum* cppEnum, enums) { + if (cppEnum->isPrivate()) + continue; + writeEnumInitialization(s, cppEnum); + } +} + +void CppGenerator::writeEnumInitialization(QTextStream& s, const AbstractMetaEnum* cppEnum) +{ + const AbstractMetaClass* enclosingClass = getProperEnclosingClassForEnum(cppEnum); + const AbstractMetaClass* upper = enclosingClass ? enclosingClass->enclosingClass() : 0; + bool hasUpperEnclosingClass = upper && upper->typeEntry()->codeGeneration() != TypeEntry::GenerateForSubclass; + QString enclosingObjectVariable; + if (enclosingClass) + enclosingObjectVariable = '&' + cpythonTypeName(enclosingClass); + else if (hasUpperEnclosingClass) + enclosingObjectVariable = "enclosingClass"; + else + enclosingObjectVariable = "module"; + + s << INDENT << "// Initialization of "; + s << (cppEnum->isAnonymous() ? "anonymous enum identified by enum value" : "enum"); + s << " '" << cppEnum->name() << "'." << endl; + + if (!cppEnum->isAnonymous()) { + FlagsTypeEntry* flags = cppEnum->typeEntry()->flags(); + if (flags) { + s << INDENT << cpythonTypeNameExt(flags) << " = PySide::QFlags::create(\"" << flags->flagsName() << "\", &" + << cpythonEnumName(cppEnum) << "_as_number);" << endl; + } + + s << INDENT << cpythonTypeNameExt(cppEnum->typeEntry()) << " = Shiboken::Enum::"; + s << ((enclosingClass || hasUpperEnclosingClass) ? "createScopedEnum" : "createGlobalEnum"); + s << '(' << enclosingObjectVariable << ',' << endl; + { + Indentation indent(INDENT); + s << INDENT << '"' << cppEnum->name() << "\"," << endl; + s << INDENT << '"' << getClassTargetFullName(cppEnum) << "\"," << endl; + s << INDENT << '"' << (cppEnum->enclosingClass() ? cppEnum->enclosingClass()->qualifiedCppName() + "::" : ""); + s << cppEnum->name() << '"'; + if (flags) + s << ',' << endl << INDENT << cpythonTypeNameExt(flags); + s << ");" << endl; + } + s << INDENT << "if (!" << cpythonTypeNameExt(cppEnum->typeEntry()) << ')' << endl; + { + Indentation indent(INDENT); + s << INDENT << "return " << m_currentErrorCode << ';' << endl << endl; + } + } + + foreach (const AbstractMetaEnumValue* enumValue, cppEnum->values()) { + if (cppEnum->typeEntry()->isEnumValueRejected(enumValue->name())) + continue; + + QString enumValueText; + if (!avoidProtectedHack() || !cppEnum->isProtected()) { + enumValueText = "(long) "; + if (cppEnum->enclosingClass()) + enumValueText += cppEnum->enclosingClass()->qualifiedCppName() + "::"; + enumValueText += enumValue->name(); + } else { + enumValueText += QString::number(enumValue->value()); + } + + if (cppEnum->isAnonymous()) { + if (enclosingClass || hasUpperEnclosingClass) { + s << INDENT << '{' << endl; + { + Indentation indent(INDENT); + s << INDENT << "PyObject* anonEnumItem = PyInt_FromLong(" << enumValueText << ");" << endl; + s << INDENT << "if (PyDict_SetItemString(((SbkObjectType*)" << enclosingObjectVariable; + s << ")->super.ht_type.tp_dict, \"" << enumValue->name() << "\", anonEnumItem) < 0)" << endl; + { + Indentation indent(INDENT); + s << INDENT << "return " << m_currentErrorCode << ';' << endl; + } + s << INDENT << "Py_DECREF(anonEnumItem);" << endl; + } + s << INDENT << '}' << endl; + } else { + s << INDENT << "if (PyModule_AddIntConstant(module, \"" << enumValue->name() << "\", "; + s << enumValueText << ") < 0)" << endl; + { + Indentation indent(INDENT); + s << INDENT << "return " << m_currentErrorCode << ';' << endl; + } + } + } else { + s << INDENT << "if (!Shiboken::Enum::"; + s << ((enclosingClass || hasUpperEnclosingClass) ? "createScopedEnumItem" : "createGlobalEnumItem"); + s << '(' << cpythonTypeNameExt(cppEnum->typeEntry()) << ',' << endl; + Indentation indent(INDENT); + s << INDENT << enclosingObjectVariable << ", \"" << enumValue->name() << "\", "; + s << enumValueText << "))" << endl; + s << INDENT << "return " << m_currentErrorCode << ';' << endl; + } + } + + writeEnumConverterInitialization(s, cppEnum); + + s << INDENT << "// End of '" << cppEnum->name() << "' enum"; + if (cppEnum->typeEntry()->flags()) + s << "/flags"; + s << '.' << endl << endl; +} + +void CppGenerator::writeSignalInitialization(QTextStream& s, const AbstractMetaClass* metaClass) +{ + // Try to check something and print some warnings + foreach (const AbstractMetaFunction* cppSignal, metaClass->cppSignalFunctions()) { + if (cppSignal->declaringClass() != metaClass) + continue; + foreach (AbstractMetaArgument* arg, cppSignal->arguments()) { + AbstractMetaType* metaType = arg->type(); + QByteArray origType = SBK_NORMALIZED_TYPE(qPrintable(metaType->originalTypeDescription())); + QByteArray cppSig = SBK_NORMALIZED_TYPE(qPrintable(metaType->cppSignature())); + if ((origType != cppSig) && (!metaType->isFlags())) + ReportHandler::warning("Typedef used on signal " + metaClass->qualifiedCppName() + "::" + cppSignal->signature()); + } + } + + s << INDENT << "PySide::Signal::registerSignals(&" << cpythonTypeName(metaClass) << ", &::" + << metaClass->qualifiedCppName() << "::staticMetaObject);" << endl; +} + +void CppGenerator::writeFlagsToLong(QTextStream& s, const AbstractMetaEnum* cppEnum) +{ + FlagsTypeEntry* flagsEntry = cppEnum->typeEntry()->flags(); + if (!flagsEntry) + return; + s << "static PyObject* " << cpythonEnumName(cppEnum) << "_long(PyObject* " PYTHON_SELF_VAR ")" << endl; + s << "{" << endl; + s << INDENT << "int val;" << endl; + AbstractMetaType* flagsType = buildAbstractMetaTypeFromTypeEntry(flagsEntry); + s << INDENT << cpythonToCppConversionFunction(flagsType) << PYTHON_SELF_VAR << ", &val);" << endl; + s << INDENT << "return Shiboken::Conversions::copyToPython(Shiboken::Conversions::PrimitiveTypeConverter(), &val);" << endl; + s << "}" << endl; +} + +void CppGenerator::writeFlagsNonZero(QTextStream& s, const AbstractMetaEnum* cppEnum) +{ + FlagsTypeEntry* flagsEntry = cppEnum->typeEntry()->flags(); + if (!flagsEntry) + return; + s << "static int " << cpythonEnumName(cppEnum) << "__nonzero(PyObject* " PYTHON_SELF_VAR ")" << endl; + s << "{" << endl; + + s << INDENT << "int val;" << endl; + AbstractMetaType* flagsType = buildAbstractMetaTypeFromTypeEntry(flagsEntry); + s << INDENT << cpythonToCppConversionFunction(flagsType) << PYTHON_SELF_VAR << ", &val);" << endl; + s << INDENT << "return val != 0;" << endl; + s << "}" << endl; +} + +void CppGenerator::writeFlagsMethods(QTextStream& s, const AbstractMetaEnum* cppEnum) +{ + writeFlagsBinaryOperator(s, cppEnum, "and", "&"); + writeFlagsBinaryOperator(s, cppEnum, "or", "|"); + writeFlagsBinaryOperator(s, cppEnum, "xor", "^"); + + writeFlagsUnaryOperator(s, cppEnum, "invert", "~"); + writeFlagsToLong(s, cppEnum); + writeFlagsNonZero(s, cppEnum); + + s << endl; +} + +void CppGenerator::writeFlagsNumberMethodsDefinition(QTextStream& s, const AbstractMetaEnum* cppEnum) +{ + QString cpythonName = cpythonEnumName(cppEnum); + + s << "static PyNumberMethods " << cpythonName << "_as_number = {" << endl; + s << INDENT << "/*nb_add*/ 0," << endl; + s << INDENT << "/*nb_subtract*/ 0," << endl; + s << INDENT << "/*nb_multiply*/ 0," << endl; + s << INDENT << "#ifndef IS_PY3K" << endl; + s << INDENT << "/* nb_divide */ 0," << endl; + s << INDENT << "#endif" << endl; + s << INDENT << "/*nb_remainder*/ 0," << endl; + s << INDENT << "/*nb_divmod*/ 0," << endl; + s << INDENT << "/*nb_power*/ 0," << endl; + s << INDENT << "/*nb_negative*/ 0," << endl; + s << INDENT << "/*nb_positive*/ 0," << endl; + s << INDENT << "/*nb_absolute*/ 0," << endl; + s << INDENT << "/*nb_nonzero*/ " << cpythonName << "__nonzero," << endl; + s << INDENT << "/*nb_invert*/ (unaryfunc)" << cpythonName << "___invert__," << endl; + s << INDENT << "/*nb_lshift*/ 0," << endl; + s << INDENT << "/*nb_rshift*/ 0," << endl; + s << INDENT << "/*nb_and*/ (binaryfunc)" << cpythonName << "___and__," << endl; + s << INDENT << "/*nb_xor*/ (binaryfunc)" << cpythonName << "___xor__," << endl; + s << INDENT << "/*nb_or*/ (binaryfunc)" << cpythonName << "___or__," << endl; + s << INDENT << "#ifndef IS_PY3K" << endl; + s << INDENT << "/* nb_coerce */ 0," << endl; + s << INDENT << "#endif" << endl; + s << INDENT << "/*nb_int*/ " << cpythonName << "_long," << endl; + s << INDENT << "#ifdef IS_PY3K" << endl; + s << INDENT << "/*nb_reserved*/ 0," << endl; + s << INDENT << "/*nb_float*/ 0," << endl; + s << INDENT << "#else" << endl; + s << INDENT << "/*nb_long*/ " << cpythonName << "_long," << endl; + s << INDENT << "/*nb_float*/ 0," << endl; + s << INDENT << "/*nb_oct*/ 0," << endl; + s << INDENT << "/*nb_hex*/ 0," << endl; + s << INDENT << "#endif" << endl; + s << INDENT << "/*nb_inplace_add*/ 0," << endl; + s << INDENT << "/*nb_inplace_subtract*/ 0," << endl; + s << INDENT << "/*nb_inplace_multiply*/ 0," << endl; + s << INDENT << "#ifndef IS_PY3K" << endl; + s << INDENT << "/*nb_inplace_divide*/ 0," << endl; + s << INDENT << "#endif" << endl; + s << INDENT << "/*nb_inplace_remainder*/ 0," << endl; + s << INDENT << "/*nb_inplace_power*/ 0," << endl; + s << INDENT << "/*nb_inplace_lshift*/ 0," << endl; + s << INDENT << "/*nb_inplace_rshift*/ 0," << endl; + s << INDENT << "/*nb_inplace_and*/ 0," << endl; + s << INDENT << "/*nb_inplace_xor*/ 0," << endl; + s << INDENT << "/*nb_inplace_or*/ 0," << endl; + s << INDENT << "/*nb_floor_divide*/ 0," << endl; + s << INDENT << "/*nb_true_divide*/ 0," << endl; + s << INDENT << "/*nb_inplace_floor_divide*/ 0," << endl; + s << INDENT << "/*nb_inplace_true_divide*/ 0," << endl; + s << INDENT << "/*nb_index*/ 0" << endl; + s << "};" << endl << endl; +} + +void CppGenerator::writeFlagsBinaryOperator(QTextStream& s, const AbstractMetaEnum* cppEnum, + QString pyOpName, QString cppOpName) +{ + FlagsTypeEntry* flagsEntry = cppEnum->typeEntry()->flags(); + Q_ASSERT(flagsEntry); + + s << "PyObject* " << cpythonEnumName(cppEnum) << "___" << pyOpName << "__(PyObject* " PYTHON_SELF_VAR ", PyObject* " PYTHON_ARG ")" << endl; + s << '{' << endl; + + AbstractMetaType* flagsType = buildAbstractMetaTypeFromTypeEntry(flagsEntry); + s << INDENT << "::" << flagsEntry->originalName() << " cppResult, " CPP_SELF_VAR ", cppArg;" << endl; + s << "#ifdef IS_PY3K" << endl; + s << INDENT << CPP_SELF_VAR " = (::" << flagsEntry->originalName() << ")PyLong_AsLong(" PYTHON_SELF_VAR ");" << endl; + s << INDENT << "cppArg = (" << flagsEntry->originalName() << ")PyLong_AsLong(" PYTHON_ARG ");" << endl; + s << "#else" << endl; + s << INDENT << CPP_SELF_VAR " = (::" << flagsEntry->originalName() << ")PyInt_AsLong(" PYTHON_SELF_VAR ");" << endl; + s << INDENT << "cppArg = (" << flagsEntry->originalName() << ")PyInt_AsLong(" PYTHON_ARG ");" << endl; + s << "#endif" << endl << endl; + s << INDENT << "cppResult = " CPP_SELF_VAR " " << cppOpName << " cppArg;" << endl; + s << INDENT << "return "; + writeToPythonConversion(s, flagsType, 0, "cppResult"); + s << ';' << endl; + s << '}' << endl << endl; +} + +void CppGenerator::writeFlagsUnaryOperator(QTextStream& s, const AbstractMetaEnum* cppEnum, + QString pyOpName, QString cppOpName, bool boolResult) +{ + FlagsTypeEntry* flagsEntry = cppEnum->typeEntry()->flags(); + Q_ASSERT(flagsEntry); + + s << "PyObject* " << cpythonEnumName(cppEnum) << "___" << pyOpName << "__(PyObject* " PYTHON_SELF_VAR ", PyObject* " PYTHON_ARG ")" << endl; + s << '{' << endl; + + AbstractMetaType* flagsType = buildAbstractMetaTypeFromTypeEntry(flagsEntry); + s << INDENT << "::" << flagsEntry->originalName() << " " CPP_SELF_VAR ";" << endl; + s << INDENT << cpythonToCppConversionFunction(flagsType) << PYTHON_SELF_VAR << ", &" CPP_SELF_VAR ");" << endl; + s << INDENT; + if (boolResult) + s << "bool"; + else + s << "::" << flagsEntry->originalName(); + s << " cppResult = " << cppOpName << CPP_SELF_VAR ";" << endl; + s << INDENT << "return "; + if (boolResult) + s << "PyBool_FromLong(cppResult)"; + else + writeToPythonConversion(s, flagsType, 0, "cppResult"); + s << ';' << endl; + s << '}' << endl << endl; +} + +void CppGenerator::writeClassRegister(QTextStream& s, const AbstractMetaClass* metaClass) +{ + const ComplexTypeEntry* classTypeEntry = metaClass->typeEntry(); + + const AbstractMetaClass* enc = metaClass->enclosingClass(); + bool hasEnclosingClass = enc && enc->typeEntry()->codeGeneration() != TypeEntry::GenerateForSubclass; + QString enclosingObjectVariable = hasEnclosingClass ? "enclosingClass" : "module"; + + QString pyTypeName = cpythonTypeName(metaClass); + s << "void init_" << metaClass->qualifiedCppName().replace("::", "_"); + s << "(PyObject* " << enclosingObjectVariable << ")" << endl; + s << '{' << endl; + + if (supportsNumberProtocol(metaClass)) { + s << INDENT << "// type has number operators" << endl; + writeTypeAsNumberDefinition(s, metaClass); + s << INDENT << pyTypeName << ".super.ht_type.tp_as_number = &" << pyTypeName << ".super.as_number;" << endl; + s << endl; + } + + if (supportsSequenceProtocol(metaClass)) { + s << INDENT << "// type supports sequence protocol" << endl; + writeTypeAsSequenceDefinition(s, metaClass); + s << INDENT << pyTypeName << ".super.ht_type.tp_as_sequence = &" << pyTypeName << ".super.as_sequence;" << endl; + s << endl; + } + + if (supportsMappingProtocol(metaClass)) { + s << INDENT << "// type supports mapping protocol" << endl; + writeTypeAsMappingDefinition(s, metaClass); + s << INDENT << pyTypeName << ".super.ht_type.tp_as_mapping = &" << pyTypeName << ".super.as_mapping;" << endl; + s << endl; + } + + s << INDENT << cpythonTypeNameExt(classTypeEntry); + s << " = reinterpret_cast(&" << pyTypeName << ");" << endl; + s << endl; + + // Multiple inheritance + QString pyTypeBasesVariable = QString("%1_bases").arg(pyTypeName); + const AbstractMetaClassList baseClasses = getBaseClasses(metaClass); + if (metaClass->baseClassNames().size() > 1) { + s << INDENT << "PyObject* " << pyTypeBasesVariable << " = PyTuple_Pack(" << baseClasses.size() << ',' << endl; + QStringList bases; + foreach (const AbstractMetaClass* base, baseClasses) + bases << "(PyObject*)" + cpythonTypeNameExt(base->typeEntry()); + Indentation indent(INDENT); + QString separator; + QTextStream sep(&separator); + sep << "," << endl << INDENT; + s << INDENT << bases.join(separator) << ");" << endl << endl; + } + + // Create type and insert it in the module or enclosing class. + s << INDENT << "if (!Shiboken::ObjectType::introduceWrapperType(" << enclosingObjectVariable; + s << ", \"" << metaClass->name() << "\", \""; + // Original name + s << metaClass->qualifiedCppName() << (isObjectType(classTypeEntry) ? "*" : ""); + s << "\"," << endl; + { + Indentation indent(INDENT); + s << INDENT << "&" << pyTypeName; + + // Set destructor function + if (!metaClass->isNamespace() && !metaClass->hasPrivateDestructor()) { + QString dtorClassName = metaClass->qualifiedCppName(); + if ((avoidProtectedHack() && metaClass->hasProtectedDestructor()) || classTypeEntry->isValue()) + dtorClassName = wrapperName(metaClass); + s << ", &Shiboken::callCppDestructor< ::" << dtorClassName << " >"; + } else if (metaClass->baseClass() || hasEnclosingClass) { + s << ", 0"; + } + + // Base type + if (metaClass->baseClass()) { + s << ", (SbkObjectType*)" << cpythonTypeNameExt(metaClass->baseClass()->typeEntry()); + // The other base types + if (metaClass->baseClassNames().size() > 1) + s << ", " << pyTypeBasesVariable; + else if (hasEnclosingClass) + s << ", 0"; + } else if (hasEnclosingClass) { + s << ", 0, 0"; + } + if (hasEnclosingClass) + s << ", true"; + s << ")) {" << endl; + s << INDENT << "return;" << endl; + } + s << INDENT << '}' << endl << endl; + + // Register conversions for the type. + writeConverterRegister(s, metaClass); + s << endl; + + // class inject-code target/beginning + if (!classTypeEntry->codeSnips().isEmpty()) { + writeCodeSnips(s, classTypeEntry->codeSnips(), CodeSnip::Beginning, TypeSystem::TargetLangCode, metaClass); + s << endl; + } + + // Fill multiple inheritance data, if needed. + const AbstractMetaClass* miClass = getMultipleInheritingClass(metaClass); + if (miClass) { + s << INDENT << "MultipleInheritanceInitFunction func = "; + if (miClass == metaClass) { + s << multipleInheritanceInitializerFunctionName(miClass) << ";" << endl; + } else { + s << "Shiboken::ObjectType::getMultipleIheritanceFunction(reinterpret_cast("; + s << cpythonTypeNameExt(miClass->typeEntry()) << "));" << endl; + } + s << INDENT << "Shiboken::ObjectType::setMultipleIheritanceFunction(&"; + s << cpythonTypeName(metaClass) << ", func);" << endl; + s << INDENT << "Shiboken::ObjectType::setCastFunction(&" << cpythonTypeName(metaClass); + s << ", &" << cpythonSpecialCastFunctionName(metaClass) << ");" << endl; + } + + // Set typediscovery struct or fill the struct of another one + if (metaClass->isPolymorphic() && metaClass->baseClass()) { + s << INDENT << "Shiboken::ObjectType::setTypeDiscoveryFunctionV2(&" << cpythonTypeName(metaClass); + s << ", &" << cpythonBaseName(metaClass) << "_typeDiscovery);" << endl << endl; + } + + AbstractMetaEnumList classEnums = metaClass->enums(); + foreach (AbstractMetaClass* innerClass, metaClass->innerClasses()) + lookForEnumsInClassesNotToBeGenerated(classEnums, innerClass); + + ErrorCode errorCode(""); + writeEnumsInitialization(s, classEnums); + + if (metaClass->hasSignals()) + writeSignalInitialization(s, metaClass); + + // Write static fields + foreach (const AbstractMetaField* field, metaClass->fields()) { + if (!field->isStatic()) + continue; + s << INDENT << "PyDict_SetItemString(" + cpythonTypeName(metaClass) + ".super.ht_type.tp_dict, \""; + s << field->name() << "\", "; + writeToPythonConversion(s, field->type(), metaClass, metaClass->qualifiedCppName() + "::" + field->name()); + s << ");" << endl; + } + s << endl; + + // class inject-code target/end + if (!classTypeEntry->codeSnips().isEmpty()) { + s << endl; + writeCodeSnips(s, classTypeEntry->codeSnips(), CodeSnip::End, TypeSystem::TargetLangCode, metaClass); + } + + if (usePySideExtensions()) { + if (avoidProtectedHack() && shouldGenerateCppWrapper(metaClass)) + s << INDENT << wrapperName(metaClass) << "::pysideInitQtMetaTypes();\n"; + else + writeInitQtMetaTypeFunctionBody(s, metaClass); + } + + if (usePySideExtensions() && metaClass->isQObject()) { + s << INDENT << "Shiboken::ObjectType::setSubTypeInitHook(&" << pyTypeName << ", &PySide::initQObjectSubType);" << endl; + s << INDENT << "PySide::initDynamicMetaObject(&" << pyTypeName << ", &::" << metaClass->qualifiedCppName() + << "::staticMetaObject, sizeof(::" << metaClass->qualifiedCppName() << "));" << endl; + } + + s << '}' << endl; +} + +void CppGenerator::writeInitQtMetaTypeFunctionBody(QTextStream& s, const AbstractMetaClass* metaClass) const +{ + // Gets all class name variants used on different possible scopes + QStringList nameVariants; + nameVariants << metaClass->name(); + const AbstractMetaClass* enclosingClass = metaClass->enclosingClass(); + while (enclosingClass) { + if (enclosingClass->typeEntry()->generateCode()) + nameVariants << (enclosingClass->name() + "::" + nameVariants.last()); + enclosingClass = enclosingClass->enclosingClass(); + } + + const QString className = metaClass->qualifiedCppName(); + if (!metaClass->isNamespace() && !metaClass->isAbstract()) { + // Qt metatypes are registered only on their first use, so we do this now. + bool canBeValue = false; + if (!isObjectType(metaClass)) { + // check if there's a empty ctor + foreach (AbstractMetaFunction* func, metaClass->functions()) { + if (func->isConstructor() && !func->arguments().count()) { + canBeValue = true; + break; + } + } + } + + if (canBeValue) { + foreach (QString name, nameVariants) + s << INDENT << "qRegisterMetaType< ::" << className << " >(\"" << name << "\");" << endl; + } + } + + foreach (AbstractMetaEnum* metaEnum, metaClass->enums()) { + if (!metaEnum->isPrivate() && !metaEnum->isAnonymous()) { + foreach (QString name, nameVariants) + s << INDENT << "qRegisterMetaType< ::" << metaEnum->typeEntry()->qualifiedCppName() << " >(\"" << name << "::" << metaEnum->name() << "\");" << endl; + + if (metaEnum->typeEntry()->flags()) { + QString n = metaEnum->typeEntry()->flags()->originalName(); + s << INDENT << "qRegisterMetaType< ::" << n << " >(\"" << n << "\");" << endl; + } + } + } +} + +void CppGenerator::writeTypeDiscoveryFunction(QTextStream& s, const AbstractMetaClass* metaClass) +{ + QString polymorphicExpr = metaClass->typeEntry()->polymorphicIdValue(); + + s << "static void* " << cpythonBaseName(metaClass) << "_typeDiscovery(void* cptr, SbkObjectType* instanceType)\n{" << endl; + + if (!polymorphicExpr.isEmpty()) { + polymorphicExpr = polymorphicExpr.replace("%1", " reinterpret_cast< ::" + metaClass->qualifiedCppName() + "*>(cptr)"); + s << INDENT << " if (" << polymorphicExpr << ")" << endl; + { + Indentation indent(INDENT); + s << INDENT << "return cptr;" << endl; + } + } else if (metaClass->isPolymorphic()) { + AbstractMetaClassList ancestors = getAllAncestors(metaClass); + foreach (AbstractMetaClass* ancestor, ancestors) { + if (ancestor->baseClass()) + continue; + if (ancestor->isPolymorphic()) { + s << INDENT << "if (instanceType == reinterpret_cast(Shiboken::SbkType< ::" + << ancestor->qualifiedCppName() << " >()))" << endl; + Indentation indent(INDENT); + s << INDENT << "return dynamic_cast< ::" << metaClass->qualifiedCppName() + << "*>(reinterpret_cast< ::"<< ancestor->qualifiedCppName() << "*>(cptr));" << endl; + } else { + ReportHandler::warning(metaClass->qualifiedCppName() + " inherits from a non polymorphic type (" + + ancestor->qualifiedCppName() + "), type discovery based on RTTI is " + "impossible, write a polymorphic-id-expression for this type."); + } + + } + } + s << INDENT << "return 0;" << endl; + s << "}\n\n"; +} + +void CppGenerator::writeSetattroFunction(QTextStream& s, const AbstractMetaClass* metaClass) +{ + s << "static int " << cpythonSetattroFunctionName(metaClass) << "(PyObject* " PYTHON_SELF_VAR ", PyObject* name, PyObject* value)" << endl; + s << '{' << endl; + if (usePySideExtensions()) { + s << INDENT << "Shiboken::AutoDecRef pp(reinterpret_cast(PySide::Property::getObject(" PYTHON_SELF_VAR ", name)));" << endl; + s << INDENT << "if (!pp.isNull())" << endl; + Indentation indent(INDENT); + s << INDENT << "return PySide::Property::setValue(reinterpret_cast(pp.object()), " PYTHON_SELF_VAR ", value);" << endl; + } + s << INDENT << "return PyObject_GenericSetAttr(" PYTHON_SELF_VAR ", name, value);" << endl; + s << '}' << endl; +} + +void CppGenerator::writeGetattroFunction(QTextStream& s, const AbstractMetaClass* metaClass) +{ + s << "static PyObject* " << cpythonGetattroFunctionName(metaClass) << "(PyObject* " PYTHON_SELF_VAR ", PyObject* name)" << endl; + s << '{' << endl; + + QString getattrFunc; + if (usePySideExtensions() && metaClass->isQObject()) { + AbstractMetaClass* qobjectClass = classes().findClass("QObject"); + getattrFunc = QString("PySide::getMetaDataFromQObject(%1, " PYTHON_SELF_VAR ", name)") + .arg(cpythonWrapperCPtr(qobjectClass, PYTHON_SELF_VAR)); + } else { + getattrFunc = "PyObject_GenericGetAttr(" PYTHON_SELF_VAR ", name)"; + } + + if (classNeedsGetattroFunction(metaClass)) { + s << INDENT << "if (" PYTHON_SELF_VAR ") {" << endl; + { + Indentation indent(INDENT); + s << INDENT << "// Search the method in the instance dict" << endl; + s << INDENT << "if (reinterpret_cast(" PYTHON_SELF_VAR ")->ob_dict) {" << endl; + { + Indentation indent(INDENT); + s << INDENT << "PyObject* meth = PyDict_GetItem(reinterpret_cast(" PYTHON_SELF_VAR ")->ob_dict, name);" << endl; + s << INDENT << "if (meth) {" << endl; + { + Indentation indent(INDENT); + s << INDENT << "Py_INCREF(meth);" << endl; + s << INDENT << "return meth;" << endl; + } + s << INDENT << '}' << endl; + } + s << INDENT << '}' << endl; + s << INDENT << "// Search the method in the type dict" << endl; + s << INDENT << "if (Shiboken::Object::isUserType(" PYTHON_SELF_VAR ")) {" << endl; + { + Indentation indent(INDENT); + s << INDENT << "PyObject* meth = PyDict_GetItem(" PYTHON_SELF_VAR "->ob_type->tp_dict, name);" << endl; + s << INDENT << "if (meth)" << endl; + { + Indentation indent(INDENT); + s << INDENT << "return PyFunction_Check(meth) ? SBK_PyMethod_New(meth, " PYTHON_SELF_VAR ") : " << getattrFunc << ';' << endl; + } + } + s << INDENT << '}' << endl; + + foreach (const AbstractMetaFunction* func, getMethodsWithBothStaticAndNonStaticMethods(metaClass)) { + s << INDENT << "if (Shiboken::String::compare(name, \"" << func->name() << "\") == 0)" << endl; + Indentation indent(INDENT); + s << INDENT << "return PyCFunction_NewEx(&" << cpythonMethodDefinitionName(func) << ", " PYTHON_SELF_VAR ", 0);" << endl; + } + } + s << INDENT << '}' << endl; + } + s << INDENT << "return " << getattrFunc << ';' << endl; + s << '}' << endl; +} + +void CppGenerator::finishGeneration() +{ + //Generate CPython wrapper file + QString classInitDecl; + QTextStream s_classInitDecl(&classInitDecl); + QString classPythonDefines; + QTextStream s_classPythonDefines(&classPythonDefines); + + QSet includes; + QString globalFunctionImpl; + QTextStream s_globalFunctionImpl(&globalFunctionImpl); + QString globalFunctionDecl; + QTextStream s_globalFunctionDef(&globalFunctionDecl); + + Indentation indent(INDENT); + + foreach (AbstractMetaFunctionList globalOverloads, getFunctionGroups().values()) { + AbstractMetaFunctionList overloads; + foreach (AbstractMetaFunction* func, globalOverloads) { + if (!func->isModifiedRemoved()) { + overloads.append(func); + if (func->typeEntry()) + includes << func->typeEntry()->include(); + } + } + + if (overloads.isEmpty()) + continue; + + writeMethodWrapper(s_globalFunctionImpl, overloads); + writeMethodDefinition(s_globalFunctionDef, overloads); + } + + //this is a temporary solution before new type revison implementation + //We need move QMetaObject register before QObject + AbstractMetaClassList lst = classes(); + AbstractMetaClass* klassQObject = lst.findClass("QObject"); + AbstractMetaClass* klassQMetaObject = lst.findClass("QMetaObject"); + if (klassQObject && klassQMetaObject) { + lst.removeAll(klassQMetaObject); + int indexOf = lst.indexOf(klassQObject); + lst.insert(indexOf, klassQMetaObject); + } + + foreach (const AbstractMetaClass* cls, lst) { + if (!shouldGenerate(cls)) + continue; + + s_classInitDecl << "void init_" << cls->qualifiedCppName().replace("::", "_") << "(PyObject* module);" << endl; + + QString defineStr = "init_" + cls->qualifiedCppName().replace("::", "_"); + + if (cls->enclosingClass() && (cls->enclosingClass()->typeEntry()->codeGeneration() != TypeEntry::GenerateForSubclass)) + defineStr += "(" + cpythonTypeNameExt(cls->enclosingClass()->typeEntry()) +"->tp_dict);"; + else + defineStr += "(module);"; + s_classPythonDefines << INDENT << defineStr << endl; + } + + QString moduleFileName(outputDirectory() + "/" + subDirectoryForPackage(packageName())); + moduleFileName += "/" + moduleName().toLower() + "_module_wrapper.cpp"; + + QFile file(moduleFileName); + verifyDirectoryFor(file); + if (!file.open(QFile::WriteOnly)) { + ReportHandler::warning("Error writing file: " + moduleFileName); + return; + } + + QTextStream s(&file); + + // write license comment + s << licenseComment() << endl; + + s << "#include " << endl; + s << "#include " << endl; + s << "#include " << endl; + if (usePySideExtensions()) + s << "#include " << endl; + + s << "#include \"" << getModuleHeaderFileName() << '"' << endl << endl; + foreach (const Include& include, includes) + s << include; + s << endl; + + // Global enums + AbstractMetaEnumList globalEnums = this->globalEnums(); + foreach (const AbstractMetaClass* metaClass, classes()) { + const AbstractMetaClass* encClass = metaClass->enclosingClass(); + if (encClass && encClass->typeEntry()->codeGeneration() != TypeEntry::GenerateForSubclass) + continue; + lookForEnumsInClassesNotToBeGenerated(globalEnums, metaClass); + } + + TypeDatabase* typeDb = TypeDatabase::instance(); + TypeSystemTypeEntry* moduleEntry = reinterpret_cast(typeDb->findType(packageName())); + + //Extra includes + s << endl << "// Extra includes" << endl; + QList extraIncludes; + if (moduleEntry) + extraIncludes = moduleEntry->extraIncludes(); + foreach (AbstractMetaEnum* cppEnum, globalEnums) + extraIncludes.append(cppEnum->typeEntry()->extraIncludes()); + qSort(extraIncludes.begin(), extraIncludes.end()); + foreach (const Include& inc, extraIncludes) + s << inc; + s << endl; + + s << "// Current module's type array." << endl; + s << "PyTypeObject** " << cppApiVariableName() << ';' << endl; + + s << "// Current module's converter array." << endl; + s << "SbkConverter** " << convertersVariableName() << ';' << endl; + + CodeSnipList snips; + if (moduleEntry) + snips = moduleEntry->codeSnips(); + + // module inject-code native/beginning + if (!snips.isEmpty()) { + writeCodeSnips(s, snips, CodeSnip::Beginning, TypeSystem::NativeCode); + s << endl; + } + + // cleanup staticMetaObject attribute + if (usePySideExtensions()) { + s << "void cleanTypesAttributes(void) {" << endl; + s << INDENT << "for (int i = 0, imax = SBK_" << moduleName() << "_IDX_COUNT; i < imax; i++) {" << endl; + { + Indentation indentation(INDENT); + s << INDENT << "PyObject *pyType = reinterpret_cast(" << cppApiVariableName() << "[i]);" << endl; + s << INDENT << "if (pyType && PyObject_HasAttrString(pyType, \"staticMetaObject\"))"<< endl; + { + Indentation indentation(INDENT); + s << INDENT << "PyObject_SetAttrString(pyType, \"staticMetaObject\", Py_None);" << endl; + } + } + s << INDENT << "}" << endl; + s << "}" << endl; + } + + s << "// Global functions "; + s << "------------------------------------------------------------" << endl; + s << globalFunctionImpl << endl; + + s << "static PyMethodDef " << moduleName() << "_methods[] = {" << endl; + s << globalFunctionDecl; + s << INDENT << "{0} // Sentinel" << endl << "};" << endl << endl; + + s << "// Classes initialization functions "; + s << "------------------------------------------------------------" << endl; + s << classInitDecl << endl; + + if (!globalEnums.isEmpty()) { + QString converterImpl; + QTextStream convImpl(&converterImpl); + + s << "// Enum definitions "; + s << "------------------------------------------------------------" << endl; + foreach (const AbstractMetaEnum* cppEnum, globalEnums) { + if (cppEnum->isAnonymous() || cppEnum->isPrivate()) + continue; + writeEnumConverterFunctions(s, cppEnum); + s << endl; + } + + if (!converterImpl.isEmpty()) { + s << "// Enum converters "; + s << "------------------------------------------------------------" << endl; + s << "namespace Shiboken" << endl << '{' << endl; + s << converterImpl << endl; + s << "} // namespace Shiboken" << endl << endl; + } + } + + QStringList requiredModules = typeDb->requiredTargetImports(); + if (!requiredModules.isEmpty()) + s << "// Required modules' type and converter arrays." << endl; + foreach (const QString& requiredModule, requiredModules) { + s << "PyTypeObject** " << cppApiVariableName(requiredModule) << ';' << endl; + s << "SbkConverter** " << convertersVariableName(requiredModule) << ';' << endl; + } + s << endl; + + s << "// Module initialization "; + s << "------------------------------------------------------------" << endl; + ExtendedConverterData extendedConverters = getExtendedConverters(); + if (!extendedConverters.isEmpty()) { + s << endl << "// Extended Converters." << endl << endl; + foreach (const TypeEntry* externalType, extendedConverters.keys()) { + s << "// Extended implicit conversions for " << externalType->qualifiedTargetLangName() << '.' << endl; + foreach (const AbstractMetaClass* sourceClass, extendedConverters[externalType]) { + AbstractMetaType* sourceType = buildAbstractMetaTypeFromAbstractMetaClass(sourceClass); + AbstractMetaType* targetType = buildAbstractMetaTypeFromTypeEntry(externalType); + writePythonToCppConversionFunctions(s, sourceType, targetType); + } + } + } + + QList typeConversions = getPrimitiveCustomConversions(); + if (!typeConversions.isEmpty()) { + s << endl << "// Primitive Type converters." << endl << endl; + foreach (const CustomConversion* conversion, typeConversions) { + s << "// C++ to Python conversion for type '" << conversion->ownerType()->qualifiedCppName() << "'." << endl; + writeCppToPythonFunction(s, conversion); + writeCustomConverterFunctions(s, conversion); + } + s << endl; + } + + QList containers = instantiatedContainers(); + if (!containers.isEmpty()) { + s << "// Container Type converters." << endl << endl; + foreach (const AbstractMetaType* container, containers) { + s << "// C++ to Python conversion for type '" << container->cppSignature() << "'." << endl; + writeContainerConverterFunctions(s, container); + } + s << endl; + } + + s << "#if defined _WIN32 || defined __CYGWIN__" << endl; + s << " #define SBK_EXPORT_MODULE __declspec(dllexport)" << endl; + s << "#elif __GNUC__ >= 4" << endl; + s << " #define SBK_EXPORT_MODULE __attribute__ ((visibility(\"default\")))" << endl; + s << "#else" << endl; + s << " #define SBK_EXPORT_MODULE" << endl; + s << "#endif" << endl << endl; + + s << "#ifdef IS_PY3K" << endl; + s << "static struct PyModuleDef moduledef = {" << endl; + s << " /* m_base */ PyModuleDef_HEAD_INIT," << endl; + s << " /* m_name */ \"" << moduleName() << "\"," << endl; + s << " /* m_doc */ 0," << endl; + s << " /* m_size */ -1," << endl; + s << " /* m_methods */ " << moduleName() << "_methods," << endl; + s << " /* m_reload */ 0," << endl; + s << " /* m_traverse */ 0," << endl; + s << " /* m_clear */ 0," << endl; + s << " /* m_free */ 0" << endl; + s << "};" << endl << endl; + s << "#endif" << endl; + s << "SBK_MODULE_INIT_FUNCTION_BEGIN(" << moduleName() << ")" << endl; + + ErrorCode errorCode("SBK_MODULE_INIT_ERROR"); + // module inject-code target/beginning + if (!snips.isEmpty()) { + writeCodeSnips(s, snips, CodeSnip::Beginning, TypeSystem::TargetLangCode); + s << endl; + } + + foreach (const QString& requiredModule, typeDb->requiredTargetImports()) { + s << INDENT << "{" << endl; + { + Indentation indentation(INDENT); + s << INDENT << "Shiboken::AutoDecRef requiredModule(Shiboken::Module::import(\"" << requiredModule << "\"));" << endl; + s << INDENT << "if (requiredModule.isNull())" << endl; + { + Indentation indentation(INDENT); + s << INDENT << "SBK_MODULE_INIT_ERROR;" << endl; + } + s << INDENT << cppApiVariableName(requiredModule) << " = Shiboken::Module::getTypes(requiredModule);" << endl; + s << INDENT << convertersVariableName(requiredModule) << " = Shiboken::Module::getTypeConverters(requiredModule);" << endl; + } + s << INDENT << "}" << endl << endl; + } + + int maxTypeIndex = getMaxTypeIndex(); + if (maxTypeIndex) { + s << INDENT << "// Create an array of wrapper types for the current module." << endl; + s << INDENT << "static PyTypeObject* cppApi[SBK_" << moduleName() << "_IDX_COUNT];" << endl; + s << INDENT << cppApiVariableName() << " = cppApi;" << endl << endl; + } + + s << INDENT << "// Create an array of primitive type converters for the current module." << endl; + s << INDENT << "static SbkConverter* sbkConverters[SBK_" << moduleName() << "_CONVERTERS_IDX_COUNT" << "];" << endl; + s << INDENT << convertersVariableName() << " = sbkConverters;" << endl << endl; + + s << "#ifdef IS_PY3K" << endl; + s << INDENT << "PyObject* module = Shiboken::Module::create(\"" << moduleName() << "\", &moduledef);" << endl; + s << "#else" << endl; + s << INDENT << "PyObject* module = Shiboken::Module::create(\"" << moduleName() << "\", "; + s << moduleName() << "_methods);" << endl; + s << "#endif" << endl << endl; + + //s << INDENT << "// Initialize converters for primitive types." << endl; + //s << INDENT << "initConverters();" << endl << endl; + + s << INDENT << "// Initialize classes in the type system" << endl; + s << classPythonDefines; + + if (!typeConversions.isEmpty()) { + s << endl; + foreach (const CustomConversion* conversion, typeConversions) { + writePrimitiveConverterInitialization(s, conversion); + s << endl; + } + } + + if (!containers.isEmpty()) { + s << endl; + foreach (const AbstractMetaType* container, containers) { + writeContainerConverterInitialization(s, container); + s << endl; + } + } + + if (!extendedConverters.isEmpty()) { + s << endl; + foreach (const TypeEntry* externalType, extendedConverters.keys()) { + writeExtendedConverterInitialization(s, externalType, extendedConverters[externalType]); + s << endl; + } + } + + writeEnumsInitialization(s, globalEnums); + + s << INDENT << "// Register primitive types converters." << endl; + foreach(const PrimitiveTypeEntry* pte, primitiveTypes()) { + if (!pte->generateCode() || !pte->isCppPrimitive()) + continue; + const TypeEntry* alias = pte->basicAliasedTypeEntry(); + if (!alias) + continue; + QString converter = converterObject(alias); + QStringList cppSignature = pte->qualifiedCppName().split("::", QString::SkipEmptyParts); + while (!cppSignature.isEmpty()) { + QString signature = cppSignature.join("::"); + s << INDENT << "Shiboken::Conversions::registerConverterName(" << converter << ", \"" << signature << "\");" << endl; + cppSignature.removeFirst(); + } + } + // Register type resolver for all containers found in signals. + QSet typeResolvers; + foreach (AbstractMetaClass* metaClass, classes()) { + if (!metaClass->isQObject() || !metaClass->typeEntry()->generateCode()) + continue; + foreach (AbstractMetaFunction* func, metaClass->functions()) { + if (func->isSignal()) { + foreach (AbstractMetaArgument* arg, func->arguments()) { + if (arg->type()->isContainer()) { + QString value = translateType(arg->type(), metaClass, ExcludeConst | ExcludeReference); + if (value.startsWith("::")) + value.remove(0, 2); + typeResolvers << SBK_NORMALIZED_TYPE(value.toAscii().constData()); + } + } + } + } + } + + s << endl; + if (maxTypeIndex) + s << INDENT << "Shiboken::Module::registerTypes(module, " << cppApiVariableName() << ");" << endl; + s << INDENT << "Shiboken::Module::registerTypeConverters(module, " << convertersVariableName() << ");" << endl; + + s << endl << INDENT << "if (PyErr_Occurred()) {" << endl; + { + Indentation indentation(INDENT); + s << INDENT << "PyErr_Print();" << endl; + s << INDENT << "Py_FatalError(\"can't initialize module " << moduleName() << "\");" << endl; + } + s << INDENT << '}' << endl; + + // module inject-code target/end + if (!snips.isEmpty()) { + writeCodeSnips(s, snips, CodeSnip::End, TypeSystem::TargetLangCode); + s << endl; + } + + // module inject-code native/end + if (!snips.isEmpty()) { + writeCodeSnips(s, snips, CodeSnip::End, TypeSystem::NativeCode); + s << endl; + } + + if (usePySideExtensions()) { + foreach (AbstractMetaEnum* metaEnum, globalEnums) + if (!metaEnum->isAnonymous()) { + s << INDENT << "qRegisterMetaType< ::" << metaEnum->typeEntry()->qualifiedCppName() << " >(\"" << metaEnum->name() << "\");" << endl; + } + + // cleanup staticMetaObject attribute + s << INDENT << "PySide::registerCleanupFunction(cleanTypesAttributes);" << endl; + } + + s << "SBK_MODULE_INIT_FUNCTION_END" << endl; +} + +static ArgumentOwner getArgumentOwner(const AbstractMetaFunction* func, int argIndex) +{ + ArgumentOwner argOwner = func->argumentOwner(func->ownerClass(), argIndex); + if (argOwner.index == ArgumentOwner::InvalidIndex) + argOwner = func->argumentOwner(func->declaringClass(), argIndex); + return argOwner; +} + +bool CppGenerator::writeParentChildManagement(QTextStream& s, const AbstractMetaFunction* func, int argIndex, bool useHeuristicPolicy) +{ + const int numArgs = func->arguments().count(); + bool ctorHeuristicEnabled = func->isConstructor() && useCtorHeuristic() && useHeuristicPolicy; + + bool usePyArgs = pythonFunctionWrapperUsesListOfArguments(OverloadData(getFunctionGroups(func->implementingClass())[func->name()], this)); + + ArgumentOwner argOwner = getArgumentOwner(func, argIndex); + ArgumentOwner::Action action = argOwner.action; + int parentIndex = argOwner.index; + int childIndex = argIndex; + if (ctorHeuristicEnabled && argIndex > 0 && numArgs) { + AbstractMetaArgument* arg = func->arguments().at(argIndex-1); + if (arg->name() == "parent" && isObjectType(arg->type())) { + action = ArgumentOwner::Add; + parentIndex = argIndex; + childIndex = -1; + } + } + + QString parentVariable; + QString childVariable; + if (action != ArgumentOwner::Invalid) { + if (!usePyArgs && argIndex > 1) + ReportHandler::warning("Argument index for parent tag out of bounds: "+func->signature()); + + if (action == ArgumentOwner::Remove) { + parentVariable = "Py_None"; + } else { + if (parentIndex == 0) + parentVariable = PYTHON_RETURN_VAR; + else if (parentIndex == -1) + parentVariable = PYTHON_SELF_VAR; + else + parentVariable = usePyArgs ? QString(PYTHON_ARGS "[%1]").arg(QString::number(parentIndex-1)) : PYTHON_ARG; + } + + if (childIndex == 0) + childVariable = PYTHON_RETURN_VAR; + else if (childIndex == -1) + childVariable = PYTHON_SELF_VAR; + else + childVariable = usePyArgs ? QString(PYTHON_ARGS "[%1]").arg(QString::number(childIndex-1)) : PYTHON_ARG; + + s << INDENT << "Shiboken::Object::setParent(" << parentVariable << ", " << childVariable << ");\n"; + return true; + } + + return false; +} + +void CppGenerator::writeParentChildManagement(QTextStream& s, const AbstractMetaFunction* func, bool useHeuristicForReturn) +{ + const int numArgs = func->arguments().count(); + + // -1 = return value + // 0 = self + // 1..n = func. args. + for (int i = -1; i <= numArgs; ++i) + writeParentChildManagement(s, func, i, useHeuristicForReturn); + + if (useHeuristicForReturn) + writeReturnValueHeuristics(s, func); +} + +void CppGenerator::writeReturnValueHeuristics(QTextStream& s, const AbstractMetaFunction* func, const QString& self) +{ + AbstractMetaType *type = func->type(); + if (!useReturnValueHeuristic() + || !func->ownerClass() + || !type + || func->isStatic() + || func->isConstructor() + || !func->typeReplaced(0).isEmpty()) { + return; + } + + ArgumentOwner argOwner = getArgumentOwner(func, ArgumentOwner::ReturnIndex); + if (argOwner.action == ArgumentOwner::Invalid || argOwner.index != ArgumentOwner::ThisIndex) { + if (isPointerToWrapperType(type)) + s << INDENT << "Shiboken::Object::setParent(" << self << ", " PYTHON_RETURN_VAR ");" << endl; + } +} + +void CppGenerator::writeHashFunction(QTextStream& s, const AbstractMetaClass* metaClass) +{ + s << "static Py_hash_t " << cpythonBaseName(metaClass) << "_HashFunc(PyObject* self) {" << endl; + writeCppSelfDefinition(s, metaClass); + s << INDENT << "return " << metaClass->typeEntry()->hashFunction() << '('; + s << (isObjectType(metaClass) ? "" : "*") << CPP_SELF_VAR << ");" << endl; + s << '}' << endl << endl; +} + +void CppGenerator::writeStdListWrapperMethods(QTextStream& s, const AbstractMetaClass* metaClass) +{ + ErrorCode errorCode(0); + + // __len__ + s << "Py_ssize_t " << cpythonBaseName(metaClass->typeEntry()) << "__len__(PyObject* " PYTHON_SELF_VAR ")" << endl; + s << '{' << endl; + writeCppSelfDefinition(s, metaClass); + s << INDENT << "return " CPP_SELF_VAR "->size();" << endl; + s << '}' << endl; + + // __getitem__ + s << "PyObject* " << cpythonBaseName(metaClass->typeEntry()) << "__getitem__(PyObject* " PYTHON_SELF_VAR ", Py_ssize_t _i)" << endl; + s << '{' << endl; + writeCppSelfDefinition(s, metaClass); + writeIndexError(s, "index out of bounds"); + + s << INDENT << metaClass->qualifiedCppName() << "::iterator _item = " CPP_SELF_VAR "->begin();" << endl; + s << INDENT << "for (Py_ssize_t pos = 0; pos < _i; pos++) _item++;" << endl; + + const AbstractMetaType* itemType = metaClass->templateBaseClassInstantiations().first(); + + s << INDENT << "return "; + writeToPythonConversion(s, itemType, metaClass, "*_item"); + s << ';' << endl; + s << '}' << endl; + + // __setitem__ + ErrorCode errorCode2(-1); + s << "int " << cpythonBaseName(metaClass->typeEntry()) << "__setitem__(PyObject* " PYTHON_SELF_VAR ", Py_ssize_t _i, PyObject* pyArg)" << endl; + s << '{' << endl; + writeCppSelfDefinition(s, metaClass); + writeIndexError(s, "list assignment index out of range"); + + s << INDENT << "PythonToCppFunc " << PYTHON_TO_CPP_VAR << ';' << endl; + s << INDENT << "if (!"; + writeTypeCheck(s, itemType, "pyArg", isNumber(itemType->typeEntry())); + s << ") {" << endl; + { + Indentation indent(INDENT); + s << INDENT << "PyErr_SetString(PyExc_TypeError, \"attributed value with wrong type, '"; + s << itemType->name() << "' or other convertible type expected\");" << endl; + s << INDENT << "return -1;" << endl; + } + s << INDENT << '}' << endl; + writeArgumentConversion(s, itemType, "cppValue", "pyArg", metaClass); + + s << INDENT << metaClass->qualifiedCppName() << "::iterator _item = " CPP_SELF_VAR "->begin();" << endl; + s << INDENT << "for (Py_ssize_t pos = 0; pos < _i; pos++) _item++;" << endl; + s << INDENT << "*_item = cppValue;" << endl; + s << INDENT << "return 0;" << endl; + s << '}' << endl; +} +void CppGenerator::writeIndexError(QTextStream& s, const QString& errorMsg) +{ + s << INDENT << "if (_i < 0 || _i >= (Py_ssize_t) " CPP_SELF_VAR "->size()) {" << endl; + { + Indentation indent(INDENT); + s << INDENT << "PyErr_SetString(PyExc_IndexError, \"" << errorMsg << "\");" << endl; + s << INDENT << "return " << m_currentErrorCode << ';' << endl; + } + s << INDENT << '}' << endl; +} + +QString CppGenerator::writeReprFunction(QTextStream& s, const AbstractMetaClass* metaClass) +{ + QString funcName = cpythonBaseName(metaClass) + "__repr__"; + s << "extern \"C\"" << endl; + s << '{' << endl; + s << "static PyObject* " << funcName << "(PyObject* self)" << endl; + s << '{' << endl; + writeCppSelfDefinition(s, metaClass); + s << INDENT << "QBuffer buffer;" << endl; + s << INDENT << "buffer.open(QBuffer::ReadWrite);" << endl; + s << INDENT << "QDebug dbg(&buffer);" << endl; + s << INDENT << "dbg << " << (metaClass->typeEntry()->isValue() ? "*" : "") << CPP_SELF_VAR ";" << endl; + s << INDENT << "buffer.close();" << endl; + s << INDENT << "QByteArray str = buffer.data();" << endl; + s << INDENT << "int idx = str.indexOf('(');" << endl; + s << INDENT << "if (idx >= 0)" << endl; + { + Indentation indent(INDENT); + s << INDENT << "str.replace(0, idx, Py_TYPE(self)->tp_name);" << endl; + } + s << INDENT << "PyObject* mod = PyDict_GetItemString(Py_TYPE(self)->tp_dict, \"__module__\");" << endl; + s << INDENT << "if (mod)" << endl; + { + Indentation indent(INDENT); + s << INDENT << "return Shiboken::String::fromFormat(\"<%s.%s at %p>\", Shiboken::String::toCString(mod), str.constData(), self);" << endl; + } + s << INDENT << "else" << endl; + { + Indentation indent(INDENT); + s << INDENT << "return Shiboken::String::fromFormat(\"<%s at %p>\", str.constData(), self);" << endl; + } + s << '}' << endl; + s << "} // extern C" << endl << endl;; + return funcName; +} diff --git a/generator/shiboken/cppgenerator.h b/generator/shiboken/cppgenerator.h new file mode 100644 index 000000000..625514996 --- /dev/null +++ b/generator/shiboken/cppgenerator.h @@ -0,0 +1,334 @@ +/* + * This file is part of the Shiboken Python Bindings Generator project. + * + * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef CPPGENERATOR_H +#define CPPGENERATOR_H + +#include "shibokengenerator.h" +#include "overloaddata.h" + +/** + * The CppGenerator generate the implementations of C++ bindings classes. + */ +class CppGenerator : public ShibokenGenerator +{ +public: + CppGenerator(); +protected: + QString fileNameForClass(const AbstractMetaClass* metaClass) const; + QList filterGroupedOperatorFunctions(const AbstractMetaClass* metaClass, + uint query); + void generateClass(QTextStream& s, const AbstractMetaClass* metaClass); + void finishGeneration(); + +private: + void writeConstructorNative(QTextStream& s, const AbstractMetaFunction* func); + void writeDestructorNative(QTextStream& s, const AbstractMetaClass* metaClass); + + QString getVirtualFunctionReturnTypeName(const AbstractMetaFunction* func); + void writeVirtualMethodNative(QTextStream& s, const AbstractMetaFunction* func); + + void writeMetaObjectMethod(QTextStream& s, const AbstractMetaClass* metaClass); + void writeMetaCast(QTextStream& s, const AbstractMetaClass* metaClass); + + void writeEnumConverterFunctions(QTextStream& s, const TypeEntry* enumType); + void writeEnumConverterFunctions(QTextStream& s, const AbstractMetaEnum* metaEnum); + void writeConverterFunctions(QTextStream& s, const AbstractMetaClass* metaClass); + void writeCustomConverterFunctions(QTextStream& s, const CustomConversion* customConversion); + void writeConverterRegister(QTextStream& s, const AbstractMetaClass* metaClass); + void writeCustomConverterRegister(QTextStream& s, const CustomConversion* customConversion, const QString& converterVar); + void writeContainerConverterRegister(QTextStream& s, const AbstractMetaType* container, const QString& converterVar); + + void writeContainerConverterFunctions(QTextStream& s, const AbstractMetaType* containerType); + + void writeMethodWrapperPreamble(QTextStream& s, OverloadData& overloadData); + void writeConstructorWrapper(QTextStream& s, const AbstractMetaFunctionList overloads); + void writeDestructorWrapper(QTextStream& s, const AbstractMetaClass* metaClass); + void writeMethodWrapper(QTextStream& s, const AbstractMetaFunctionList overloads); + void writeArgumentsInitializer(QTextStream& s, OverloadData& overloadData); + void writeCppSelfDefinition(QTextStream& s, const AbstractMetaFunction* func, bool hasStaticOverload = false); + void writeCppSelfDefinition(QTextStream& s, const AbstractMetaClass* metaClass, bool hasStaticOverload = false, bool cppSelfAsReference = false); + + void writeErrorSection(QTextStream& s, OverloadData& overloadData); + void writeFunctionReturnErrorCheckSection(QTextStream& s, bool hasReturnValue = true); + + /// Writes the check section for the validity of wrapped C++ objects. + void writeInvalidPyObjectCheck(QTextStream& s, const QString& pyObj); + + void writeTypeCheck(QTextStream& s, const AbstractMetaType* argType, QString argumentName, bool isNumber = false, QString customType = "", bool rejectNull = false); + void writeTypeCheck(QTextStream& s, const OverloadData* overloadData, QString argumentName); + + void writeTypeDiscoveryFunction(QTextStream& s, const AbstractMetaClass* metaClass); + + void writeSetattroFunction(QTextStream& s, const AbstractMetaClass* metaClass); + void writeGetattroFunction(QTextStream& s, const AbstractMetaClass* metaClass); + + /** + * Writes Python to C++ conversions for arguments on Python wrappers. + * If implicit conversions, and thus new object allocation, are needed, + * code to deallocate a possible new instance is also generated. + * \param s text stream to write + * \param argType a pointer to the argument type to be converted + * \param argName C++ argument name + * \param pyArgName Python argument name + * \param context the current meta class + * \param defaultValue an optional default value to be used instead of the conversion result + * \param castArgumentAsUnused if true the converted argument is cast as unused to avoid compiler warnings + */ + void writeArgumentConversion(QTextStream& s, const AbstractMetaType* argType, + const QString& argName, const QString& pyArgName, + const AbstractMetaClass* context = 0, + const QString& defaultValue = QString(), + bool castArgumentAsUnused = false); + + /** + * Returns the AbstractMetaType for a function argument. + * If the argument type was modified in the type system, this method will + * try to build a new type based on the type name defined in the type system. + * \param func The function which owns the argument. + * \param argPos Argument position in the function signature. + * Note that the position 0 represents the return value, and the function + * parameters start counting on 1. + * \param newType It is set to true if the type returned is a new object that must be deallocated. + * \return The type of the argument indicated by \p argPos. + */ + const AbstractMetaType* getArgumentType(const AbstractMetaFunction* func, int argPos); + + void writePythonToCppTypeConversion(QTextStream& s, + const AbstractMetaType* type, + const QString& pyIn, + const QString& cppOut, + const AbstractMetaClass* context = 0, + const QString& defaultValue = QString()); + + /// Writes the conversion rule for arguments of regular and virtual methods. + void writeConversionRule(QTextStream& s, const AbstractMetaFunction* func, TypeSystem::Language language); + /// Writes the conversion rule for the return value of a method. + void writeConversionRule(QTextStream& s, const AbstractMetaFunction* func, TypeSystem::Language language, const QString& outputVar); + + /** + * Set the Python method wrapper return value variable to Py_None if + * there are return types different from void in any of the other overloads + * for the function passed as parameter. + * \param s text stream to write + * \param func a pointer to the function that will possibly return Py_None + * \param thereIsReturnValue indicates if the return type of any of the other overloads + * for this function is different from 'void' + */ + void writeNoneReturn(QTextStream& s, const AbstractMetaFunction* func, bool thereIsReturnValue); + + /** + * Writes the Python function wrapper overload decisor that selects which C++ + * method/function to call with the received Python arguments. + * \param s text stream to write + * \param overloadData the overload data describing all the possible overloads for the function/method + */ + void writeOverloadedFunctionDecisor(QTextStream& s, const OverloadData& overloadData); + /// Recursive auxiliar method to the other writeOverloadedFunctionDecisor. + void writeOverloadedFunctionDecisorEngine(QTextStream& s, const OverloadData* parentOverloadData); + + /// Writes calls to all the possible method/function overloads. + void writeFunctionCalls(QTextStream& s, const OverloadData& overloadData); + + /// Writes the call to a single function usually from a collection of overloads. + void writeSingleFunctionCall(QTextStream& s, const OverloadData& overloadData, const AbstractMetaFunction* func); + + /// Returns the name of a C++ to Python conversion function. + static QString cppToPythonFunctionName(const QString& sourceTypeName, QString targetTypeName = QString()); + + /// Returns the name of a Python to C++ conversion function. + static QString pythonToCppFunctionName(const QString& sourceTypeName, const QString& targetTypeName); + static QString pythonToCppFunctionName(const AbstractMetaType* sourceType, const AbstractMetaType* targetType); + static QString pythonToCppFunctionName(const CustomConversion::TargetToNativeConversion* toNative, const TypeEntry* targetType); + + /// Returns the name of a Python to C++ convertible check function. + static QString convertibleToCppFunctionName(const QString& sourceTypeName, const QString& targetTypeName); + static QString convertibleToCppFunctionName(const AbstractMetaType* sourceType, const AbstractMetaType* targetType); + static QString convertibleToCppFunctionName(const CustomConversion::TargetToNativeConversion* toNative, const TypeEntry* targetType); + + /// Writes a C++ to Python conversion function. + void writeCppToPythonFunction(QTextStream& s, const QString& code, const QString& sourceTypeName, QString targetTypeName = QString()); + void writeCppToPythonFunction(QTextStream& s, const CustomConversion* customConversion); + void writeCppToPythonFunction(QTextStream& s, const AbstractMetaType* containerType); + + /// Writes a Python to C++ conversion function. + void writePythonToCppFunction(QTextStream& s, const QString& code, const QString& sourceTypeName, const QString& targetTypeName); + + /// Writes a Python to C++ convertible check function. + void writeIsPythonConvertibleToCppFunction(QTextStream& s, + const QString& sourceTypeName, + const QString& targetTypeName, + const QString& condition, + QString pythonToCppFuncName = QString(), + bool acceptNoneAsCppNull = false); + + /// Writes a pair of Python to C++ conversion and check functions. + void writePythonToCppConversionFunctions(QTextStream& s, + const AbstractMetaType* sourceType, + const AbstractMetaType* targetType, + QString typeCheck = QString(), + QString conversion = QString(), + QString preConversion = QString()); + /// Writes a pair of Python to C++ conversion and check functions for implicit conversions. + void writePythonToCppConversionFunctions(QTextStream& s, + const CustomConversion::TargetToNativeConversion* toNative, + const TypeEntry* targetType); + + /// Writes a pair of Python to C++ conversion and check functions for instantiated container types. + void writePythonToCppConversionFunctions(QTextStream& s, const AbstractMetaType* containerType); + + void writeAddPythonToCppConversion(QTextStream& s, const QString& converterVar, const QString& pythonToCppFunc, const QString& isConvertibleFunc); + + void writeNamedArgumentResolution(QTextStream& s, const AbstractMetaFunction* func, bool usePyArgs); + + /// Returns a string containing the name of an argument for the given function and argument index. + QString argumentNameFromIndex(const AbstractMetaFunction* func, int argIndex, const AbstractMetaClass** wrappedClass); + void writeMethodCall(QTextStream& s, const AbstractMetaFunction* func, int maxArgs = 0); + + void writeClassRegister(QTextStream& s, const AbstractMetaClass* metaClass); + void writeClassDefinition(QTextStream& s, const AbstractMetaClass* metaClass); + void writeMethodDefinitionEntry(QTextStream& s, const AbstractMetaFunctionList overloads); + void writeMethodDefinition(QTextStream& s, const AbstractMetaFunctionList overloads); + + /// Writes the implementation of all methods part of python sequence protocol + void writeSequenceMethods(QTextStream& s, const AbstractMetaClass* metaClass); + void writeTypeAsSequenceDefinition(QTextStream& s, const AbstractMetaClass* metaClass); + + /// Writes the PyMappingMethods structure for types that supports the python mapping protocol. + void writeTypeAsMappingDefinition(QTextStream& s, const AbstractMetaClass* metaClass); + void writeMappingMethods(QTextStream& s, const AbstractMetaClass* metaClass); + + void writeTypeAsNumberDefinition(QTextStream& s, const AbstractMetaClass* metaClass); + + void writeTpTraverseFunction(QTextStream& s, const AbstractMetaClass* metaClass); + void writeTpClearFunction(QTextStream& s, const AbstractMetaClass* metaClass); + + void writeCopyFunction(QTextStream& s, const AbstractMetaClass *metaClass); + + void writeGetterFunction(QTextStream& s, const AbstractMetaField* metaField); + void writeSetterFunction(QTextStream& s, const AbstractMetaField* metaField); + + void writeRichCompareFunction(QTextStream& s, const AbstractMetaClass* metaClass); + void writeToPythonFunction(QTextStream& s, const AbstractMetaClass* metaClass); + + void writeEnumsInitialization(QTextStream& s, AbstractMetaEnumList& enums); + void writeEnumInitialization(QTextStream& s, const AbstractMetaEnum* metaEnum); + + void writeSignalInitialization(QTextStream& s, const AbstractMetaClass* metaClass); + + void writeFlagsMethods(QTextStream& s, const AbstractMetaEnum* cppEnum); + void writeFlagsToLong(QTextStream& s, const AbstractMetaEnum* cppEnum); + void writeFlagsNonZero(QTextStream& s, const AbstractMetaEnum* cppEnum); + void writeFlagsNumberMethodsDefinition(QTextStream& s, const AbstractMetaEnum* cppEnum); + void writeFlagsBinaryOperator(QTextStream& s, const AbstractMetaEnum* cppEnum, + QString pyOpName, QString cppOpName); + void writeFlagsUnaryOperator(QTextStream& s, const AbstractMetaEnum* cppEnum, + QString pyOpName, QString cppOpName, bool boolResult = false); + + /// Writes the function that registers the multiple inheritance information for the classes that need it. + void writeMultipleInheritanceInitializerFunction(QTextStream& s, const AbstractMetaClass* metaClass); + /// Writes the implementation of special cast functions, used when we need to cast a class with multiple inheritance. + void writeSpecialCastFunction(QTextStream& s, const AbstractMetaClass* metaClass); + + void writePrimitiveConverterInitialization(QTextStream& s, const CustomConversion* customConversion); + void writeEnumConverterInitialization(QTextStream& s, const TypeEntry* enumType); + void writeEnumConverterInitialization(QTextStream& s, const AbstractMetaEnum* metaEnum); + void writeContainerConverterInitialization(QTextStream& s, const AbstractMetaType* type); + void writeExtendedConverterInitialization(QTextStream& s, const TypeEntry* externalType, const QList& conversions); + + void writeParentChildManagement(QTextStream& s, const AbstractMetaFunction* func, bool userHeuristicForReturn); + bool writeParentChildManagement(QTextStream& s, const AbstractMetaFunction* func, int argIndex, bool userHeuristicPolicy); + void writeReturnValueHeuristics(QTextStream& s, const AbstractMetaFunction* func, const QString& self = PYTHON_SELF_VAR); + void writeInitQtMetaTypeFunctionBody(QTextStream& s, const AbstractMetaClass* metaClass) const; + + /** + * Returns the multiple inheritance initializer function for the given class. + * \param metaClass the class for whom the function name must be generated. + * \return name of the multiple inheritance information initializer function or + * an empty string if there is no multiple inheritance in its ancestry. + */ + QString multipleInheritanceInitializerFunctionName(const AbstractMetaClass* metaClass); + + /// Returns a list of all classes to which the given class could be cast. + QStringList getAncestorMultipleInheritance(const AbstractMetaClass* metaClass); + + /// Returns true if the given class supports the python number protocol + bool supportsNumberProtocol(const AbstractMetaClass* metaClass); + + /// Returns true if the given class supports the python sequence protocol + bool supportsSequenceProtocol(const AbstractMetaClass* metaClass); + + /// Returns true if the given class supports the python mapping protocol + bool supportsMappingProtocol(const AbstractMetaClass* metaClass); + + /// Returns true if generator should produce getters and setters for the given class. + bool shouldGenerateGetSetList(const AbstractMetaClass* metaClass); + + void writeHashFunction(QTextStream& s, const AbstractMetaClass* metaClass); + + /// Write default implementations for sequence protocol + void writeStdListWrapperMethods(QTextStream& s, const AbstractMetaClass* metaClass); + /// Helper function for writeStdListWrapperMethods. + void writeIndexError(QTextStream& s, const QString& errorMsg); + + QString writeReprFunction(QTextStream& s, const AbstractMetaClass* metaClass); + + bool hasBoolCast(const AbstractMetaClass* metaClass) const; + + // Number protocol structure members names. + static QHash m_nbFuncs; + + // Maps special function names to function parameters and return types + // used by CPython API in the sequence protocol. + QHash > m_sequenceProtocol; + // Sequence protocol structure members names. + static QHash m_sqFuncs; + + // Maps special function names to function parameters and return types + // used by CPython API in the mapping protocol. + QHash > m_mappingProtocol; + // Mapping protocol structure members names. + static QHash m_mpFuncs; + + static QString m_currentErrorCode; + + /// Helper class to set and restore the current error code. + class ErrorCode { + public: + explicit ErrorCode(QString errorCode) { + m_savedErrorCode = CppGenerator::m_currentErrorCode; + CppGenerator::m_currentErrorCode = errorCode; + } + explicit ErrorCode(int errorCode) { + m_savedErrorCode = CppGenerator::m_currentErrorCode; + CppGenerator::m_currentErrorCode = QString::number(errorCode); + } + ~ErrorCode() { + CppGenerator::m_currentErrorCode = m_savedErrorCode; + } + private: + QString m_savedErrorCode; + }; +}; + +#endif // CPPGENERATOR_H diff --git a/generator/shiboken/headergenerator.cpp b/generator/shiboken/headergenerator.cpp new file mode 100644 index 000000000..9be272067 --- /dev/null +++ b/generator/shiboken/headergenerator.cpp @@ -0,0 +1,494 @@ +/* + * This file is part of the Shiboken Python Bindings Generator project. + * + * Copyright (C) 2009-2012 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "headergenerator.h" +#include +#include +#include + +#include +#include +#include +#include +#include + +QString HeaderGenerator::fileNameForClass(const AbstractMetaClass* metaClass) const +{ + return metaClass->qualifiedCppName().toLower().replace("::", "_") + QLatin1String("_wrapper.h"); +} + +void HeaderGenerator::writeCopyCtor(QTextStream& s, const AbstractMetaClass* metaClass) const +{ + s << INDENT << wrapperName(metaClass) << "(const " << metaClass->qualifiedCppName() << "& self)"; + s << " : " << metaClass->qualifiedCppName() << "(self)" << endl; + s << INDENT << "{" << endl; + s << INDENT << "}" << endl << endl; +} + +void HeaderGenerator::writeProtectedFieldAccessors(QTextStream& s, const AbstractMetaField* field) const +{ + AbstractMetaType *metaType = field->type(); + QString fieldType = metaType->cppSignature(); + QString fieldName = field->enclosingClass()->qualifiedCppName() + "::" + field->name(); + + // Force use of pointer to return internal variable memory + bool useReference = (!metaType->isConstant() && + !metaType->isEnum() && + !metaType->isPrimitive() && + metaType->indirections() == 0); + + + // Get function + s << INDENT << "inline " << fieldType + << (useReference ? '*' : ' ') + << ' ' << protectedFieldGetterName(field) << "()" + << " { return " + << (useReference ? '&' : ' ') << "this->" << fieldName << "; }" << endl; + + // Set function + s << INDENT << "inline void " << protectedFieldSetterName(field) << '(' << fieldType << " value)" + << " { " << fieldName << " = value; }" << endl; +} + +void HeaderGenerator::generateClass(QTextStream& s, const AbstractMetaClass* metaClass) +{ + ReportHandler::debugSparse("Generating header for " + metaClass->fullName()); + m_inheritedOverloads.clear(); + Indentation indent(INDENT); + + // write license comment + s << licenseComment(); + + QString wrapperName = HeaderGenerator::wrapperName(metaClass); + QString headerGuard = wrapperName.replace("::", "_").toUpper(); + + // Header + s << "#ifndef SBK_" << headerGuard << "_H" << endl; + s << "#define SBK_" << headerGuard << "_H" << endl<< endl; + + if (!avoidProtectedHack()) + s << "#define protected public" << endl << endl; + + s << "#include " << endl << endl; + + //Includes + s << metaClass->typeEntry()->include() << endl; + + if (shouldGenerateCppWrapper(metaClass)) { + + if (usePySideExtensions() && metaClass->isQObject()) + s << "namespace PySide { class DynamicQMetaObject; }\n\n"; + + // Class + s << "class " << wrapperName; + s << " : public " << metaClass->qualifiedCppName(); + + s << endl << '{' << endl << "public:" << endl; + + bool hasVirtualFunction = false; + foreach (AbstractMetaFunction *func, filterFunctions(metaClass)) { + if (func->isVirtual()) + hasVirtualFunction = true; + writeFunction(s, func); + } + + if (avoidProtectedHack() && metaClass->hasProtectedFields()) { + foreach (AbstractMetaField* field, metaClass->fields()) { + if (!field->isProtected()) + continue; + writeProtectedFieldAccessors(s, field); + } + } + + //destructor + if (!avoidProtectedHack() || !metaClass->hasPrivateDestructor()) { + s << INDENT; + if (metaClass->hasVirtualDestructor() || hasVirtualFunction) + s << "virtual "; + s << "~" << wrapperName << "();" << endl; + } + + writeCodeSnips(s, metaClass->typeEntry()->codeSnips(), CodeSnip::Declaration, TypeSystem::NativeCode); + + if ((!avoidProtectedHack() || !metaClass->hasPrivateDestructor()) + && usePySideExtensions() && metaClass->isQObject()) { + s << "public:\n"; + s << INDENT << "virtual int qt_metacall(QMetaObject::Call call, int id, void** args);" << endl; + s << INDENT << "virtual void* qt_metacast(const char* _clname);" << endl; + } + + if (m_inheritedOverloads.size()) { + s << INDENT << "// Inherited overloads, because the using keyword sux" << endl; + writeInheritedOverloads(s); + } + + if (usePySideExtensions()) + s << INDENT << "static void pysideInitQtMetaTypes();" << endl; + + s << "};" << endl << endl; + } + + s << "#endif // SBK_" << headerGuard << "_H" << endl << endl; +} + +void HeaderGenerator::writeFunction(QTextStream& s, const AbstractMetaFunction* func) +{ + + // do not write copy ctors here. + if (!func->isPrivate() && func->isCopyConstructor()) { + writeCopyCtor(s, func->ownerClass()); + return; + } + if (func->isUserAdded()) + return; + + if (avoidProtectedHack() && func->isProtected() && !func->isConstructor() && !func->isOperatorOverload()) { + s << INDENT << "inline " << (func->isStatic() ? "static " : ""); + s << functionSignature(func, "", "_protected", Generator::EnumAsInts|Generator::OriginalTypeDescription) << " { "; + s << (func->type() ? "return " : ""); + if (!func->isAbstract()) + s << func->ownerClass()->qualifiedCppName() << "::"; + s << func->originalName() << '('; + QStringList args; + foreach (const AbstractMetaArgument* arg, func->arguments()) { + QString argName = arg->name(); + const TypeEntry* enumTypeEntry = 0; + if (arg->type()->isFlags()) + enumTypeEntry = reinterpret_cast(arg->type()->typeEntry())->originator(); + else if (arg->type()->isEnum()) + enumTypeEntry = arg->type()->typeEntry(); + if (enumTypeEntry) + argName = QString("%1(%2)").arg(arg->type()->cppSignature()).arg(argName); + args << argName; + } + s << args.join(", ") << ')'; + s << "; }" << endl; + } + + // pure virtual functions need a default implementation + if ((func->isPrivate() && !visibilityModifiedToPrivate(func)) + || (func->isModifiedRemoved() && !func->isAbstract())) + return; + + if (avoidProtectedHack() && func->ownerClass()->hasPrivateDestructor() + && (func->isAbstract() || func->isVirtual())) + return; + + if (func->isConstructor() || func->isAbstract() || func->isVirtual()) { + s << INDENT; + Options virtualOption = Generator::OriginalTypeDescription; + + if (func->isVirtual() || func->isAbstract()) + s << "virtual "; + else if (!func->hasSignatureModifications()) + virtualOption = Generator::NoOption; + + s << functionSignature(func, "", "", virtualOption) << ';' << endl; + + // Check if this method hide other methods in base classes + foreach (const AbstractMetaFunction* f, func->ownerClass()->functions()) { + if (f != func + && !f->isConstructor() + && !f->isPrivate() + && !f->isVirtual() + && !f->isAbstract() + && !f->isStatic() + && f->name() == func->name()) { + m_inheritedOverloads << f; + break; + } + } + + // TODO: when modified an abstract method ceases to be virtual but stays abstract + //if (func->isModifiedRemoved() && func->isAbstract()) { + //} + } +} + +static void _writeTypeIndexDefineLine(QTextStream& s, const QString& variableName, int typeIndex) +{ + s << "#define "; + s.setFieldWidth(60); + s << variableName; + s.setFieldWidth(0); + s << ' ' << typeIndex << endl; +} +void HeaderGenerator::writeTypeIndexDefineLine(QTextStream& s, const TypeEntry* typeEntry) +{ + if (!typeEntry || !typeEntry->generateCode()) + return; + s.setFieldAlignment(QTextStream::AlignLeft); + int typeIndex = getTypeIndex(typeEntry); + _writeTypeIndexDefineLine(s, getTypeIndexVariableName(typeEntry), typeIndex); + if (typeEntry->isComplex()) { + const ComplexTypeEntry* cType = reinterpret_cast(typeEntry); + if (cType->baseContainerType()) { + const AbstractMetaClass* metaClass = classes().findClass(cType); + if (metaClass->templateBaseClass()) + _writeTypeIndexDefineLine(s, getTypeIndexVariableName(metaClass, true), typeIndex); + } + } + if (typeEntry->isEnum()) { + const EnumTypeEntry* ete = reinterpret_cast(typeEntry); + if (ete->flags()) + writeTypeIndexDefineLine(s, ete->flags()); + } +} + +void HeaderGenerator::writeTypeIndexDefine(QTextStream& s, const AbstractMetaClass* metaClass) +{ + if (!metaClass->typeEntry()->generateCode()) + return; + writeTypeIndexDefineLine(s, metaClass->typeEntry()); + foreach (const AbstractMetaEnum* metaEnum, metaClass->enums()) { + if (metaEnum->isPrivate()) + continue; + writeTypeIndexDefineLine(s, metaEnum->typeEntry()); + } +} + +void HeaderGenerator::finishGeneration() +{ + // Generate the main header for this module. + // This header should be included by binding modules + // extendind on top of this one. + QSet includes; + QString macros; + QTextStream macrosStream(¯os); + QString sbkTypeFunctions; + QTextStream typeFunctions(&sbkTypeFunctions); + QString protectedEnumSurrogates; + QTextStream protEnumsSurrogates(&protectedEnumSurrogates); + + Indentation indent(INDENT); + + macrosStream << "// Type indices" << endl; + AbstractMetaEnumList globalEnums = this->globalEnums(); + foreach (const AbstractMetaClass* metaClass, classes()) { + writeTypeIndexDefine(macrosStream, metaClass); + lookForEnumsInClassesNotToBeGenerated(globalEnums, metaClass); + } + foreach (const AbstractMetaEnum* metaEnum, globalEnums) + writeTypeIndexDefineLine(macrosStream, metaEnum->typeEntry()); + macrosStream << "#define "; + macrosStream.setFieldWidth(60); + macrosStream << "SBK_"+moduleName()+"_IDX_COUNT"; + macrosStream.setFieldWidth(0); + macrosStream << ' ' << getMaxTypeIndex() << endl << endl; + macrosStream << "// This variable stores all Python types exported by this module." << endl; + macrosStream << "extern PyTypeObject** " << cppApiVariableName() << ';' << endl << endl; + macrosStream << "// This variable stores all type converters exported by this module." << endl; + macrosStream << "extern SbkConverter** " << convertersVariableName() << ';' << endl << endl;; + + // TODO-CONVERTER ------------------------------------------------------------------------------ + // Using a counter would not do, a fix must be made to APIExtractor's getTypeIndex(). + macrosStream << "// Converter indices" << endl; + QList primitives = primitiveTypes(); + int pCount = 0; + foreach (const PrimitiveTypeEntry* ptype, primitives) { + /* Note: do not generate indices for typedef'd primitive types + * as they'll use the primitive type converters instead, so we + * don't need to create any other. + */ + if (!ptype->generateCode() || !ptype->customConversion()) + continue; + + _writeTypeIndexDefineLine(macrosStream, getTypeIndexVariableName(ptype), pCount++); + } + + foreach (const AbstractMetaType* container, instantiatedContainers()) { + //_writeTypeIndexDefineLine(macrosStream, getTypeIndexVariableName(container), pCount); + // DEBUG + QString variableName = getTypeIndexVariableName(container); + macrosStream << "#define "; + macrosStream.setFieldWidth(60); + macrosStream << variableName; + macrosStream.setFieldWidth(0); + macrosStream << ' ' << pCount << " // " << container->cppSignature() << endl; + // DEBUG + pCount++; + } + + // Because on win32 the compiler will not accept a zero length array. + if (pCount == 0) + pCount++; + _writeTypeIndexDefineLine(macrosStream, QString("SBK_%1_CONVERTERS_IDX_COUNT").arg(moduleName()), pCount); + macrosStream << endl; + // TODO-CONVERTER ------------------------------------------------------------------------------ + + macrosStream << "// Macros for type check" << endl; + foreach (const AbstractMetaEnum* cppEnum, globalEnums) { + if (cppEnum->isAnonymous() || cppEnum->isPrivate()) + continue; + includes << cppEnum->typeEntry()->include(); + writeProtectedEnumSurrogate(protEnumsSurrogates, cppEnum); + writeSbkTypeFunction(typeFunctions, cppEnum); + } + + foreach (AbstractMetaClass* metaClass, classes()) { + if (!shouldGenerate(metaClass)) + continue; + + //Includes + const TypeEntry* classType = metaClass->typeEntry(); + includes << classType->include(); + + foreach (const AbstractMetaEnum* cppEnum, metaClass->enums()) { + if (cppEnum->isAnonymous() || cppEnum->isPrivate()) + continue; + EnumTypeEntry* enumType = cppEnum->typeEntry(); + includes << enumType->include(); + writeProtectedEnumSurrogate(protEnumsSurrogates, cppEnum); + writeSbkTypeFunction(typeFunctions, cppEnum); + } + + if (!metaClass->isNamespace()) + writeSbkTypeFunction(typeFunctions, metaClass); + } + + QString moduleHeaderFileName(outputDirectory() + + QDir::separator() + subDirectoryForPackage(packageName()) + + QDir::separator() + getModuleHeaderFileName()); + + QString includeShield("SBK_" + moduleName().toUpper() + "_PYTHON_H"); + + FileOut file(moduleHeaderFileName); + QTextStream& s = file.stream; + // write license comment + s << licenseComment() << endl << endl; + + s << "#ifndef " << includeShield << endl; + s << "#define " << includeShield << endl<< endl; + if (!avoidProtectedHack()) { + s << "//workaround to access protected functions" << endl; + s << "#define protected public" << endl << endl; + } + + s << "#include " << endl; + s << "#include " << endl; + s << "#include " << endl; + s << "#include " << endl; + s << "#include " << endl; + s << "#include " << endl << endl; + if (usePySideExtensions()) + s << "#include " << endl; + + QStringList requiredTargetImports = TypeDatabase::instance()->requiredTargetImports(); + if (!requiredTargetImports.isEmpty()) { + s << "// Module Includes" << endl; + foreach (const QString& requiredModule, requiredTargetImports) + s << "#include <" << getModuleHeaderFileName(requiredModule) << ">" << endl; + s << endl; + } + + s << "// Binded library includes" << endl; + foreach (const Include& include, includes) + s << include; + + if (!primitiveTypes().isEmpty()) { + s << "// Conversion Includes - Primitive Types" << endl; + foreach (const PrimitiveTypeEntry* ptype, primitiveTypes()) + s << ptype->include(); + s << endl; + } + + if (!containerTypes().isEmpty()) { + s << "// Conversion Includes - Container Types" << endl; + foreach (const ContainerTypeEntry* ctype, containerTypes()) + s << ctype->include(); + s << endl; + } + + s << macros << endl; + + if (!protectedEnumSurrogates.isEmpty()) { + s << "// Protected enum surrogates" << endl; + s << protectedEnumSurrogates << endl; + } + + s << "namespace Shiboken" << endl << '{' << endl << endl; + + s << "// PyType functions, to get the PyObjectType for a type T\n"; + s << sbkTypeFunctions << endl; + + s << "} // namespace Shiboken" << endl << endl; + + s << "#endif // " << includeShield << endl << endl; +} + +void HeaderGenerator::writeProtectedEnumSurrogate(QTextStream& s, const AbstractMetaEnum* cppEnum) +{ + if (avoidProtectedHack() && cppEnum->isProtected()) + s << "enum " << protectedEnumSurrogateName(cppEnum) << " {};" << endl; +} + +void HeaderGenerator::writeSbkTypeFunction(QTextStream& s, const AbstractMetaEnum* cppEnum) +{ + QString enumName; + if (avoidProtectedHack() && cppEnum->isProtected()) { + enumName = protectedEnumSurrogateName(cppEnum); + } else { + enumName = cppEnum->name(); + if (cppEnum->enclosingClass()) + enumName = cppEnum->enclosingClass()->qualifiedCppName() + "::" + enumName; + } + + s << "template<> inline PyTypeObject* SbkType< ::" << enumName << " >() "; + s << "{ return " << cpythonTypeNameExt(cppEnum->typeEntry()) << "; }\n"; + + FlagsTypeEntry* flag = cppEnum->typeEntry()->flags(); + if (flag) { + s << "template<> inline PyTypeObject* SbkType< ::" << flag->name() << " >() " + << "{ return " << cpythonTypeNameExt(flag) << "; }\n"; + } +} + +void HeaderGenerator::writeSbkTypeFunction(QTextStream& s, const AbstractMetaClass* cppClass) +{ + s << "template<> inline PyTypeObject* SbkType< ::" << cppClass->qualifiedCppName() << " >() " + << "{ return reinterpret_cast(" << cpythonTypeNameExt(cppClass->typeEntry()) << "); }\n"; +} + +void HeaderGenerator::writeInheritedOverloads(QTextStream& s) +{ + foreach (const AbstractMetaFunction* func, m_inheritedOverloads) { + s << INDENT << "inline "; + s << functionSignature(func, "", "", Generator::EnumAsInts|Generator::OriginalTypeDescription) << " { "; + s << (func->type() ? "return " : ""); + s << func->ownerClass()->qualifiedCppName() << "::" << func->originalName() << '('; + QStringList args; + foreach (const AbstractMetaArgument* arg, func->arguments()) { + QString argName = arg->name(); + const TypeEntry* enumTypeEntry = 0; + if (arg->type()->isFlags()) + enumTypeEntry = reinterpret_cast(arg->type()->typeEntry())->originator(); + else if (arg->type()->isEnum()) + enumTypeEntry = arg->type()->typeEntry(); + if (enumTypeEntry) + argName = QString("%1(%2)").arg(arg->type()->cppSignature()).arg(argName); + args << argName; + } + s << args.join(", ") << ')'; + s << "; }" << endl; + } +} diff --git a/generator/shiboken/headergenerator.h b/generator/shiboken/headergenerator.h new file mode 100644 index 000000000..e830efaee --- /dev/null +++ b/generator/shiboken/headergenerator.h @@ -0,0 +1,56 @@ +/* + * This file is part of the Shiboken Python Bindings Generator project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef HEADERGENERATOR_H +#define HEADERGENERATOR_H + +#include "shibokengenerator.h" + +/** + * The HeaderGenerator generate the declarations of C++ bindings classes. + */ +class HeaderGenerator : public ShibokenGenerator +{ +public: + QMap options() const { return QMap(); } +protected: + QString fileNameForClass(const AbstractMetaClass* metaClass) const; + void generateClass(QTextStream& s, const AbstractMetaClass* metaClass); + void finishGeneration(); + +private: + void writeCopyCtor(QTextStream &s, const AbstractMetaClass* metaClass) const; + void writeProtectedFieldAccessors(QTextStream& s, const AbstractMetaField* field) const; + void writeFunction(QTextStream& s, const AbstractMetaFunction* func); + void writeSbkTypeFunction(QTextStream& s, const AbstractMetaEnum* cppEnum); + void writeSbkTypeFunction(QTextStream& s, const AbstractMetaClass* cppClass); + void writeTypeIndexDefineLine(QTextStream& s, const TypeEntry* typeEntry); + void writeTypeIndexDefine(QTextStream& s, const AbstractMetaClass* metaClass); + void writeProtectedEnumSurrogate(QTextStream& s, const AbstractMetaEnum* cppEnum); + void writeInheritedOverloads(QTextStream& s); + + QSet m_inheritedOverloads; +}; + +#endif // HEADERGENERATOR_H + diff --git a/generator/shiboken/overloaddata.cpp b/generator/shiboken/overloaddata.cpp new file mode 100644 index 000000000..57084558e --- /dev/null +++ b/generator/shiboken/overloaddata.cpp @@ -0,0 +1,1002 @@ +/* + * This file is part of the Shiboken Python Bindings Generator project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include +#include "overloaddata.h" +#include "shibokengenerator.h" +#include + +static const TypeEntry* getAliasedTypeEntry(const TypeEntry* typeEntry) +{ + if (typeEntry->isPrimitive()) { + const PrimitiveTypeEntry* pte = reinterpret_cast(typeEntry); + while (pte->aliasedTypeEntry()) + pte = pte->aliasedTypeEntry(); + typeEntry = pte; + } + return typeEntry; +} + +static QString getTypeName(const AbstractMetaType* type) +{ + const TypeEntry* typeEntry = getAliasedTypeEntry(type->typeEntry()); + QString typeName = typeEntry->name(); + if (typeEntry->isContainer()) { + QStringList types; + foreach (const AbstractMetaType* cType, type->instantiations()) { + const TypeEntry* typeEntry = getAliasedTypeEntry(cType->typeEntry()); + types << typeEntry->name(); + } + typeName += QString("<%1 >").arg(types.join(",")); + } + return typeName; +} + +static QString getTypeName(const OverloadData* ov) +{ + return ov->hasArgumentTypeReplace() ? ov->argumentTypeReplaced() : getTypeName(ov->argType()); +} + +static bool typesAreEqual(const AbstractMetaType* typeA, const AbstractMetaType* typeB) +{ + if (typeA->typeEntry() == typeB->typeEntry()) { + if (typeA->isContainer()) { + if (typeA->instantiations().size() != typeB->instantiations().size()) + return false; + + for (int i = 0; i < typeA->instantiations().size(); ++i) { + if (!typesAreEqual(typeA->instantiations().at(i), typeB->instantiations().at(i))) + return false; + } + return true; + } + + return !(ShibokenGenerator::isCString(typeA) ^ ShibokenGenerator::isCString(typeB)); + } + return false; +} + + +/** + * OverloadSortData just helps writing clearer code in the + * OverloadData::sortNextOverloads method. + */ +struct OverloadSortData +{ + OverloadSortData() : counter(0) {}; + + /** + * Adds a typeName into the type map without associating it with + * a OverloadData. This is done to express type dependencies that could + * or could not appear in overloaded signatures not processed yet. + */ + void mapType(const QString& typeName) + { + if (map.contains(typeName)) + return; + map[typeName] = counter; + if (!reverseMap.contains(counter)) + reverseMap[counter] = 0; + counter++; + } + + void mapType(OverloadData* overloadData) + { + QString typeName = getTypeName(overloadData); + map[typeName] = counter; + reverseMap[counter] = overloadData; + counter++; + } + + int lastProcessedItemId() { return counter - 1; } + + int counter; + QHash map; // typeName -> id + QHash reverseMap; // id -> OverloadData; +}; + +/** + * Helper function that returns the name of a container get from containerType argument and + * an instantiation taken either from an implicit conversion expressed by the function argument, + * or from the string argument implicitConvTypeName. + */ +static QString getImplicitConversionTypeName(const AbstractMetaType* containerType, + const AbstractMetaType* instantiation, + const AbstractMetaFunction* function, + const QString& implicitConvTypeName = QString()) +{ + QString impConv; + if (!implicitConvTypeName.isEmpty()) + impConv = implicitConvTypeName; + else if (function->isConversionOperator()) + impConv = function->ownerClass()->typeEntry()->name(); + else + impConv = getTypeName(function->arguments().first()->type()); + + QStringList types; + foreach (const AbstractMetaType* otherType, containerType->instantiations()) + types << (otherType == instantiation ? impConv : getTypeName(otherType)); + + const ContainerTypeEntry* containerTypeEntry = reinterpret_cast(containerType->typeEntry()); + return containerTypeEntry->qualifiedCppName() + '<' + types.join(", ") + " >"; +} + +/** + * Topologically sort the overloads by implicit convertion order + * + * This avoids using an implicit conversion if there's an explicit + * overload for the convertible type. So, if there's an implicit convert + * like TargetType(ConvertibleType foo) and both are in the overload list, + * ConvertibleType is checked before TargetType. + * + * Side effects: Modifies m_nextOverloadData + */ +void OverloadData::sortNextOverloads() +{ + OverloadSortData sortData; + bool checkPyObject = false; + int pyobjectIndex = 0; + bool checkPySequence = false; + int pySeqIndex = 0; + bool checkQString = false; + int qstringIndex = 0; + bool checkQVariant = false; + int qvariantIndex = 0; + bool checkPyBuffer = false; + int pyBufferIndex = 0; + + // Primitive types that are not int, long, short, + // char and their respective unsigned counterparts. + QStringList nonIntegerPrimitives; + nonIntegerPrimitives << "float" << "double" << "bool"; + + // Signed integer primitive types. + QStringList signedIntegerPrimitives; + signedIntegerPrimitives << "int" << "short" << "long"; + + // sort the children overloads + foreach(OverloadData *ov, m_nextOverloadData) + ov->sortNextOverloads(); + + if (m_nextOverloadData.size() <= 1) + return; + + // Populates the OverloadSortData object containing map and reverseMap, to map type names to ids, + // these ids will be used by the topological sort algorithm, because is easier and faster to work + // with graph sorting using integers. + foreach(OverloadData* ov, m_nextOverloadData) { + sortData.mapType(ov); + + const QString typeName(getTypeName(ov)); + + if (!checkPyObject && typeName.contains("PyObject")) { + checkPyObject = true; + pyobjectIndex = sortData.lastProcessedItemId(); + } else if (!checkPySequence && typeName == "PySequence") { + checkPySequence = true; + pySeqIndex = sortData.lastProcessedItemId(); + } else if (!checkPyBuffer && typeName == "PyBuffer") { + checkPyBuffer = true; + pyBufferIndex = sortData.lastProcessedItemId(); + } else if (!checkQVariant && typeName == "QVariant") { + checkQVariant = true; + qvariantIndex = sortData.lastProcessedItemId(); + } else if (!checkQString && typeName == "QString") { + checkQString = true; + qstringIndex = sortData.lastProcessedItemId(); + } + + foreach (const AbstractMetaType* instantiation, ov->argType()->instantiations()) { + // Add dependencies for type instantiation of container. + QString typeName = getTypeName(instantiation); + sortData.mapType(typeName); + + // Build dependency for implicit conversion types instantiations for base container. + // For example, considering signatures "method(list)" and "method(list)", + // and being PointF implicitly convertible from Point, an list instantiation with T + // as Point must come before the PointF instantiation, or else list will never + // be called. In the case of primitive types, list must come before list. + if (instantiation->isPrimitive() && (signedIntegerPrimitives.contains(instantiation->name()))) { + foreach (const QString& primitive, nonIntegerPrimitives) + sortData.mapType(getImplicitConversionTypeName(ov->argType(), instantiation, 0, primitive)); + } else { + foreach (const AbstractMetaFunction* function, m_generator->implicitConversions(instantiation)) + sortData.mapType(getImplicitConversionTypeName(ov->argType(), instantiation, function)); + } + } + } + + + // Create the graph of type dependencies based on implicit conversions. + Graph graph(sortData.reverseMap.count()); + // All C++ primitive types, add any forgotten type AT THE END OF THIS LIST! + const char* primitiveTypes[] = {"int", + "unsigned int", + "long", + "unsigned long", + "short", + "unsigned short", + "bool", + "unsigned char", + "char", + "float", + "double", + "const char*" + }; + const int numPrimitives = sizeof(primitiveTypes)/sizeof(const char*); + bool hasPrimitive[numPrimitives]; + for (int i = 0; i < numPrimitives; ++i) + hasPrimitive[i] = sortData.map.contains(primitiveTypes[i]); + + if (checkPySequence && checkPyObject) + graph.addEdge(pySeqIndex, pyobjectIndex); + + QStringList classesWithIntegerImplicitConversion; + + foreach(OverloadData* ov, m_nextOverloadData) { + const AbstractMetaType* targetType = ov->argType(); + const QString targetTypeEntryName(getTypeName(ov)); + int targetTypeId = sortData.map[targetTypeEntryName]; + + // Process implicit conversions + foreach(AbstractMetaFunction* function, m_generator->implicitConversions(targetType)) { + QString convertibleType; + if (function->isConversionOperator()) + convertibleType = function->ownerClass()->typeEntry()->name(); + else + convertibleType = getTypeName(function->arguments().first()->type()); + + if (convertibleType == "int" || convertibleType == "unsigned int") + classesWithIntegerImplicitConversion << targetTypeEntryName; + + if (!sortData.map.contains(convertibleType)) + continue; + + int convertibleTypeId = sortData.map[convertibleType]; + + // If a reverse pair already exists, remove it. Probably due to the + // container check (This happened to QVariant and QHash) + graph.removeEdge(targetTypeId, convertibleTypeId); + graph.addEdge(convertibleTypeId, targetTypeId); + } + + // Process inheritance relationships + if (targetType->isValue() || targetType->isObject()) { + const AbstractMetaClass* metaClass = m_generator->classes().findClass(targetType->typeEntry()); + foreach (const AbstractMetaClass* ancestor, m_generator->getAllAncestors(metaClass)) { + QString ancestorTypeName = ancestor->typeEntry()->name(); + if (!sortData.map.contains(ancestorTypeName)) + continue; + int ancestorTypeId = sortData.map[ancestorTypeName]; + graph.removeEdge(ancestorTypeId, targetTypeId); + graph.addEdge(targetTypeId, ancestorTypeId); + } + } + + // Process template instantiations + foreach (const AbstractMetaType* instantiation, targetType->instantiations()) { + if (sortData.map.contains(getTypeName(instantiation))) { + int convertible = sortData.map[getTypeName(instantiation)]; + + if (!graph.containsEdge(targetTypeId, convertible)) // Avoid cyclic dependency. + graph.addEdge(convertible, targetTypeId); + + if (instantiation->isPrimitive() && (signedIntegerPrimitives.contains(instantiation->name()))) { + foreach (const QString& primitive, nonIntegerPrimitives) { + QString convertibleTypeName = getImplicitConversionTypeName(ov->argType(), instantiation, 0, primitive); + if (!graph.containsEdge(targetTypeId, sortData.map[convertibleTypeName])) // Avoid cyclic dependency. + graph.addEdge(sortData.map[convertibleTypeName], targetTypeId); + } + + } else { + foreach (const AbstractMetaFunction* function, m_generator->implicitConversions(instantiation)) { + QString convertibleTypeName = getImplicitConversionTypeName(ov->argType(), instantiation, function); + if (!graph.containsEdge(targetTypeId, sortData.map[convertibleTypeName])) // Avoid cyclic dependency. + graph.addEdge(sortData.map[convertibleTypeName], targetTypeId); + } + } + } + } + + + if ((checkPySequence || checkPyObject || checkPyBuffer) + && !targetTypeEntryName.contains("PyObject") + && !targetTypeEntryName.contains("PyBuffer") + && !targetTypeEntryName.contains("PySequence")) { + if (checkPySequence) { + // PySequence will be checked after all more specific types, but before PyObject. + graph.addEdge(targetTypeId, pySeqIndex); + } else if (checkPyBuffer) { + // PySequence will be checked after all more specific types, but before PyObject. + graph.addEdge(targetTypeId, pyBufferIndex); + } else { + // Add dependency on PyObject, so its check is the last one (too generic). + graph.addEdge(targetTypeId, pyobjectIndex); + } + } else if (checkQVariant && targetTypeEntryName != "QVariant") { + if (!graph.containsEdge(qvariantIndex, targetTypeId)) // Avoid cyclic dependency. + graph.addEdge(targetTypeId, qvariantIndex); + } else if (checkQString && ShibokenGenerator::isPointer(ov->argType()) + && targetTypeEntryName != "QString" + && targetTypeEntryName != "QByteArray" + && (!checkPyObject || targetTypeId != pyobjectIndex)) { + if (!graph.containsEdge(qstringIndex, targetTypeId)) // Avoid cyclic dependency. + graph.addEdge(targetTypeId, qstringIndex); + } + + if (targetType->isEnum()) { + // Enum values must precede primitive types. + for (int i = 0; i < numPrimitives; ++i) { + if (hasPrimitive[i]) + graph.addEdge(targetTypeId, sortData.map[primitiveTypes[i]]); + } + } + } + + // QByteArray args need to be checked after QString args + if (sortData.map.contains("QString") && sortData.map.contains("QByteArray")) + graph.addEdge(sortData.map["QString"], sortData.map["QByteArray"]); + + foreach(OverloadData* ov, m_nextOverloadData) { + const AbstractMetaType* targetType = ov->argType(); + if (!targetType->isEnum()) + continue; + + QString targetTypeEntryName = getTypeName(targetType); + // Enum values must precede types implicitly convertible from "int" or "unsigned int". + foreach (const QString& implicitFromInt, classesWithIntegerImplicitConversion) + graph.addEdge(sortData.map[targetTypeEntryName], sortData.map[implicitFromInt]); + } + + + // Special case for double(int i) (not tracked by m_generator->implicitConversions + foreach (const QString& signedIntegerName, signedIntegerPrimitives) { + if (sortData.map.contains(signedIntegerName)) { + foreach (const QString& nonIntegerName, nonIntegerPrimitives) { + if (sortData.map.contains(nonIntegerName)) + graph.addEdge(sortData.map[nonIntegerName], sortData.map[signedIntegerName]); + } + } + } + + // sort the overloads topologically based on the dependency graph. + QLinkedList unmappedResult = graph.topologicalSort(); + if (unmappedResult.isEmpty()) { + QString funcName = referenceFunction()->name(); + if (referenceFunction()->ownerClass()) + funcName.prepend(referenceFunction()->ownerClass()->name() + '.'); + + // Dump overload graph + QString graphName = QDir::tempPath() + '/' + funcName + ".dot"; + QHash::const_iterator it = sortData.map.begin(); + QHash nodeNames; + for (; it != sortData.map.end(); ++it) + nodeNames.insert(it.value(), it.key()); + graph.dumpDot(nodeNames, graphName); + ReportHandler::warning(QString("Cyclic dependency found on overloaddata for '%1' method! The graph boy saved the graph at %2.").arg(qPrintable(funcName)).arg(qPrintable(graphName))); + } + + m_nextOverloadData.clear(); + foreach(int i, unmappedResult) { + if (!sortData.reverseMap[i]) + continue; + m_nextOverloadData << sortData.reverseMap[i]; + } +} + +/** + * Root constructor for OverloadData + * + * This constructor receives the list of overloads for a given function and iterates generating + * the graph of OverloadData instances. Each OverloadData instance references an argument/type + * combination. + * + * Example: + * addStuff(double, PyObject *) + * addStuff(double, int) + * + * Given these two overloads, there will be the following graph: + * + * addStuff - double - PyObject* + * \- int + * + */ +OverloadData::OverloadData(const AbstractMetaFunctionList& overloads, const ShibokenGenerator* generator) + : m_minArgs(256), m_maxArgs(0), m_argPos(-1), m_argType(0), + m_headOverloadData(this), m_previousOverloadData(0), m_generator(generator) +{ + foreach (const AbstractMetaFunction* func, overloads) { + m_overloads.append(func); + int argSize = func->arguments().size() - numberOfRemovedArguments(func); + if (m_minArgs > argSize) + m_minArgs = argSize; + else if (m_maxArgs < argSize) + m_maxArgs = argSize; + OverloadData* currentOverloadData = this; + foreach (const AbstractMetaArgument* arg, func->arguments()) { + if (func->argumentRemoved(arg->argumentIndex() + 1)) + continue; + currentOverloadData = currentOverloadData->addOverloadData(func, arg); + } + } + + // Sort the overload possibilities so that the overload decisor code goes for the most + // important cases first, based on the topological order of the implicit conversions + sortNextOverloads(); + + // Fix minArgs + if (minArgs() > maxArgs()) + m_headOverloadData->m_minArgs = maxArgs(); +} + +OverloadData::OverloadData(OverloadData* headOverloadData, const AbstractMetaFunction* func, + const AbstractMetaType* argType, int argPos) + : m_minArgs(256), m_maxArgs(0), m_argPos(argPos), m_argType(argType), + m_headOverloadData(headOverloadData), m_previousOverloadData(0) +{ + if (func) + this->addOverload(func); +} + +void OverloadData::addOverload(const AbstractMetaFunction* func) +{ + int origNumArgs = func->arguments().size(); + int removed = numberOfRemovedArguments(func); + int numArgs = origNumArgs - removed; + + if (numArgs > m_headOverloadData->m_maxArgs) + m_headOverloadData->m_maxArgs = numArgs; + + if (numArgs < m_headOverloadData->m_minArgs) + m_headOverloadData->m_minArgs = numArgs; + + for (int i = 0; m_headOverloadData->m_minArgs > 0 && i < origNumArgs; i++) { + if (func->argumentRemoved(i + 1)) + continue; + if (!ShibokenGenerator::getDefaultValue(func, func->arguments()[i]).isEmpty()) { + int fixedArgIndex = i - removed; + if (fixedArgIndex < m_headOverloadData->m_minArgs) + m_headOverloadData->m_minArgs = fixedArgIndex; + } + } + + m_overloads.append(func); +} + +OverloadData* OverloadData::addOverloadData(const AbstractMetaFunction* func, + const AbstractMetaArgument* arg) +{ + const AbstractMetaType* argType = arg->type(); + OverloadData* overloadData = 0; + if (!func->isOperatorOverload()) { + foreach (OverloadData* tmp, m_nextOverloadData) { + // TODO: 'const char *', 'char *' and 'char' will have the same TypeEntry? + + // If an argument have a type replacement, then we should create a new overloaddata + // for it, unless the next argument also have a identical type replacement. + QString replacedArg = func->typeReplaced(tmp->m_argPos + 1); + bool argsReplaced = !replacedArg.isEmpty() || !tmp->m_argTypeReplaced.isEmpty(); + if ((!argsReplaced && typesAreEqual(tmp->m_argType, argType)) + || (argsReplaced && replacedArg == tmp->argumentTypeReplaced())) { + tmp->addOverload(func); + overloadData = tmp; + } + } + } + + if (!overloadData) { + overloadData = new OverloadData(m_headOverloadData, func, argType, m_argPos + 1); + overloadData->m_previousOverloadData = this; + overloadData->m_generator = this->m_generator; + QString typeReplaced = func->typeReplaced(arg->argumentIndex() + 1); + + if (!typeReplaced.isEmpty()) + overloadData->m_argTypeReplaced = typeReplaced; + m_nextOverloadData.append(overloadData); + } + + return overloadData; +} + +QStringList OverloadData::returnTypes() const +{ + QSet retTypes; + foreach (const AbstractMetaFunction* func, m_overloads) { + if (!func->typeReplaced(0).isEmpty()) + retTypes << func->typeReplaced(0); + else if (func->type() && !func->argumentRemoved(0)) + retTypes << func->type()->cppSignature(); + else + retTypes << "void"; + } + return QStringList(retTypes.toList()); +} + +bool OverloadData::hasNonVoidReturnType() const +{ + QStringList retTypes = returnTypes(); + return !retTypes.contains("void") || retTypes.size() > 1; +} + +bool OverloadData::hasVarargs() const +{ + foreach (const AbstractMetaFunction* func, m_overloads) { + AbstractMetaArgumentList args = func->arguments(); + if (args.size() > 1 && args.last()->type()->isVarargs()) + return true; + } + return false; +} + +bool OverloadData::hasAllowThread() const +{ + foreach (const AbstractMetaFunction* func, m_overloads) { + if (func->allowThread()) + return true; + } + return false; +} + +bool OverloadData::hasStaticFunction(const AbstractMetaFunctionList& overloads) +{ + foreach (const AbstractMetaFunction* func, overloads) { + if (func->isStatic()) + return true; + } + return false; +} + +bool OverloadData::hasStaticFunction() const +{ + foreach (const AbstractMetaFunction* func, m_overloads) { + if (func->isStatic()) + return true; + } + return false; +} + +bool OverloadData::hasInstanceFunction(const AbstractMetaFunctionList& overloads) +{ + foreach (const AbstractMetaFunction* func, overloads) { + if (!func->isStatic()) + return true; + } + return false; +} + +bool OverloadData::hasInstanceFunction() const +{ + foreach (const AbstractMetaFunction* func, m_overloads) { + if (!func->isStatic()) + return true; + } + return false; +} + +bool OverloadData::hasStaticAndInstanceFunctions(const AbstractMetaFunctionList& overloads) +{ + return OverloadData::hasStaticFunction(overloads) && OverloadData::hasInstanceFunction(overloads); +} + +bool OverloadData::hasStaticAndInstanceFunctions() const +{ + return OverloadData::hasStaticFunction() && OverloadData::hasInstanceFunction(); +} + +const AbstractMetaFunction* OverloadData::referenceFunction() const +{ + return m_overloads.first(); +} + +const AbstractMetaArgument* OverloadData::argument(const AbstractMetaFunction* func) const +{ + if (isHeadOverloadData() || !m_overloads.contains(func)) + return 0; + + int argPos = 0; + int removed = 0; + for (int i = 0; argPos <= m_argPos; i++) { + if (func->argumentRemoved(i + 1)) + removed++; + else + argPos++; + } + + return func->arguments()[m_argPos + removed]; +} + +OverloadDataList OverloadData::overloadDataOnPosition(OverloadData* overloadData, int argPos) const +{ + OverloadDataList overloadDataList; + if (overloadData->argPos() == argPos) { + overloadDataList.append(overloadData); + } else if (overloadData->argPos() < argPos) { + foreach (OverloadData* pd, overloadData->nextOverloadData()) + overloadDataList += overloadDataOnPosition(pd, argPos); + } + return overloadDataList; +} + +OverloadDataList OverloadData::overloadDataOnPosition(int argPos) const +{ + OverloadDataList overloadDataList; + overloadDataList += overloadDataOnPosition(m_headOverloadData, argPos); + return overloadDataList; +} + +bool OverloadData::nextArgumentHasDefaultValue() const +{ + foreach (OverloadData* overloadData, m_nextOverloadData) { + if (overloadData->getFunctionWithDefaultValue()) + return true; + } + return false; +} + +static OverloadData* _findNextArgWithDefault(OverloadData* overloadData) +{ + if (overloadData->getFunctionWithDefaultValue()) + return overloadData; + + OverloadData* result = 0; + foreach (OverloadData* odata, overloadData->nextOverloadData()) { + OverloadData* tmp = _findNextArgWithDefault(odata); + if (!result || (tmp && result->argPos() > tmp->argPos())) + result = tmp; + } + return result; +} + +OverloadData* OverloadData::findNextArgWithDefault() +{ + return _findNextArgWithDefault(this); +} + +bool OverloadData::isFinalOccurrence(const AbstractMetaFunction* func) const +{ + foreach (const OverloadData* pd, m_nextOverloadData) { + if (pd->overloads().contains(func)) + return false; + } + return true; +} + +QList OverloadData::overloadsWithoutRepetition() const +{ + QList overloads = m_overloads; + foreach (const AbstractMetaFunction* func, m_overloads) { + if (func->minimalSignature().endsWith("const")) + continue; + foreach (const AbstractMetaFunction* f, overloads) { + if ((func->minimalSignature() + "const") == f->minimalSignature()) { + overloads.removeOne(f); + break; + } + } + } + return overloads; +} + +const AbstractMetaFunction* OverloadData::getFunctionWithDefaultValue() const +{ + foreach (const AbstractMetaFunction* func, m_overloads) { + int removedArgs = 0; + for (int i = 0; i <= m_argPos + removedArgs; i++) { + if (func->argumentRemoved(i + 1)) + removedArgs++; + } + if (!ShibokenGenerator::getDefaultValue(func, func->arguments()[m_argPos + removedArgs]).isEmpty()) + return func; + } + return 0; +} + +QList OverloadData::invalidArgumentLengths() const +{ + QSet validArgLengths; + + foreach (const AbstractMetaFunction* func, m_headOverloadData->m_overloads) { + const AbstractMetaArgumentList args = func->arguments(); + int offset = 0; + for (int i = 0; i < args.size(); ++i) { + if (func->argumentRemoved(i+1)) { + offset++; + } else { + if (!ShibokenGenerator::getDefaultValue(func, args[i]).isEmpty()) + validArgLengths << i-offset; + } + } + validArgLengths << args.size() - offset; + } + + QList invalidArgLengths; + for (int i = minArgs() + 1; i < maxArgs(); i++) { + if (!validArgLengths.contains(i)) + invalidArgLengths.append(i); + } + + return invalidArgLengths; +} + +int OverloadData::numberOfRemovedArguments(const AbstractMetaFunction* func, int finalArgPos) +{ + int removed = 0; + if (finalArgPos < 0) { + for (int i = 0; i < func->arguments().size(); i++) { + if (func->argumentRemoved(i + 1)) + removed++; + } + } else { + for (int i = 0; i < finalArgPos + removed; i++) { + if (func->argumentRemoved(i + 1)) + removed++; + } + } + return removed; +} + +QPair OverloadData::getMinMaxArguments(const AbstractMetaFunctionList& overloads) +{ + int minArgs = 10000; + int maxArgs = 0; + for (int i = 0; i < overloads.size(); i++) { + const AbstractMetaFunction* func = overloads[i]; + int origNumArgs = func->arguments().size(); + int removed = numberOfRemovedArguments(func); + int numArgs = origNumArgs - removed; + if (maxArgs < numArgs) + maxArgs = numArgs; + if (minArgs > numArgs) + minArgs = numArgs; + for (int j = 0; j < origNumArgs; j++) { + if (func->argumentRemoved(j + 1)) + continue; + int fixedArgIndex = j - removed; + if (fixedArgIndex < minArgs && !ShibokenGenerator::getDefaultValue(func, func->arguments()[j]).isEmpty()) + minArgs = fixedArgIndex; + } + } + return QPair(minArgs, maxArgs); +} + +bool OverloadData::isSingleArgument(const AbstractMetaFunctionList& overloads) +{ + bool singleArgument = true; + foreach (const AbstractMetaFunction* func, overloads) { + if (func->arguments().size() - numberOfRemovedArguments(func) != 1) { + singleArgument = false; + break; + } + } + return singleArgument; +} + +void OverloadData::dumpGraph(QString filename) const +{ + QFile file(filename); + if (file.open(QFile::WriteOnly)) { + QTextStream s(&file); + s << m_headOverloadData->dumpGraph(); + } +} + +QString OverloadData::dumpGraph() const +{ + QString indent(4, ' '); + QString result; + QTextStream s(&result); + if (m_argPos == -1) { + const AbstractMetaFunction* rfunc = referenceFunction(); + s << "digraph OverloadedFunction {" << endl; + s << indent << "graph [fontsize=12 fontname=freemono labelloc=t splines=true overlap=false rankdir=LR];" << endl; + + // Shows all function signatures + s << "legend [fontsize=9 fontname=freemono shape=rect label=\""; + foreach (const AbstractMetaFunction* func, overloads()) { + s << "f" << functionNumber(func) << " : "; + if (func->type()) + s << func->type()->cppSignature().replace('<', "<").replace('>', ">"); + else + s << "void"; + s << ' ' << func->minimalSignature().replace('<', "<").replace('>', ">") << "\\l"; + } + s << "\"];" << endl; + + // Function box title + s << indent << '"' << rfunc->name() << "\" [shape=plaintext style=\"filled,bold\" margin=0 fontname=freemono fillcolor=white penwidth=1 "; + s << "label=<"; + s << ""; + + // Function return type + s << ""; + + // Shows type changes for all function signatures + foreach (const AbstractMetaFunction* func, overloads()) { + if (func->typeReplaced(0).isEmpty()) + continue; + s << ""; + } + + // Minimum and maximum number of arguments + s << ""; + s << ""; + + if (rfunc->ownerClass()) { + if (rfunc->implementingClass() != rfunc->ownerClass()) + s << ""; + if (rfunc->declaringClass() != rfunc->ownerClass() && rfunc->declaringClass() != rfunc->implementingClass()) + s << ""; + } + + // Overloads for the signature to present point + s << ""; + + s << "
"; + if (rfunc->ownerClass()) + s << rfunc->ownerClass()->name() << "::"; + s << rfunc->name().replace('<', "<").replace('>', ">") << ""; + if (rfunc->isVirtual()) { + s << "
<<"; + if (rfunc->isAbstract()) + s << "pure "; + s << "virtual>>"; + } + s << "
original type"; + if (rfunc->type()) + s << rfunc->type()->cppSignature().replace('<', "<").replace('>', ">"); + else + s << "void"; + s << "
f" << functionNumber(func); + s << "-type"; + s << func->typeReplaced(0).replace('<', "<").replace('>', ">") << "
minArgs"; + s << minArgs() << "
maxArgs"; + s << maxArgs() << "
implementor" << rfunc->implementingClass()->name() << "
declarator" << rfunc->declaringClass()->name() << "
overloads"; + foreach (const AbstractMetaFunction* func, overloads()) + s << 'f' << functionNumber(func) << ' '; + s << "
> ];" << endl; + + foreach (const OverloadData* pd, nextOverloadData()) + s << indent << '"' << rfunc->name() << "\" -> " << pd->dumpGraph(); + + s << "}" << endl; + } else { + QString argId = QString("arg_%1").arg((ulong)this); + s << argId << ';' << endl; + + s << indent << '"' << argId << "\" [shape=\"plaintext\" style=\"filled,bold\" margin=\"0\" fontname=\"freemono\" fillcolor=\"white\" penwidth=1 "; + s << "label=<"; + + // Argument box title + s << ""; + + // Argument type information + QString type = hasArgumentTypeReplace() ? argumentTypeReplaced() : argType()->cppSignature(); + s << ""; + if (hasArgumentTypeReplace()) { + s << ""; + } + + // Overloads for the signature to present point + s << ""; + + // Show default values (original and modified) for various functions + foreach (const AbstractMetaFunction* func, overloads()) { + const AbstractMetaArgument* arg = argument(func); + if (!arg) + continue; + QString argDefault = ShibokenGenerator::getDefaultValue(func, arg); + if (!argDefault.isEmpty() || + argDefault != arg->originalDefaultValueExpression()) { + s << ""; + } + if (argDefault != arg->originalDefaultValueExpression()) { + s << ""; + } + } + + s << "
"; + s << "arg #" << argPos() << "
type"; + s << type.replace("&", "&") << "
orig. type"; + s << argType()->cppSignature().replace("&", "&") << "
overloads"; + foreach (const AbstractMetaFunction* func, overloads()) + s << 'f' << functionNumber(func) << ' '; + s << "
f" << functionNumber(func); + s << "-default"; + s << argDefault << "
f" << functionNumber(func); + s << "-orig-default"; + s << arg->originalDefaultValueExpression() << "
>];" << endl; + + foreach (const OverloadData* pd, nextOverloadData()) + s << indent << argId << " -> " << pd->dumpGraph(); + } + return result; +} + +int OverloadData::functionNumber(const AbstractMetaFunction* func) const +{ + return m_headOverloadData->m_overloads.indexOf(func); +} + +OverloadData::~OverloadData() +{ + while (!m_nextOverloadData.isEmpty()) + delete m_nextOverloadData.takeLast(); +} + +bool OverloadData::hasArgumentTypeReplace() const +{ + return !m_argTypeReplaced.isEmpty(); +} + +QString OverloadData::argumentTypeReplaced() const +{ + return m_argTypeReplaced; +} + +bool OverloadData::hasArgumentWithDefaultValue(const AbstractMetaFunctionList& overloads) +{ + if (OverloadData::getMinMaxArguments(overloads).second == 0) + return false; + foreach (const AbstractMetaFunction* func, overloads) { + if (hasArgumentWithDefaultValue(func)) + return true; + } + return false; +} + +bool OverloadData::hasArgumentWithDefaultValue() const +{ + if (maxArgs() == 0) + return false; + foreach (const AbstractMetaFunction* func, overloads()) { + if (hasArgumentWithDefaultValue(func)) + return true; + } + return false; +} + +bool OverloadData::hasArgumentWithDefaultValue(const AbstractMetaFunction* func) +{ + foreach (const AbstractMetaArgument* arg, func->arguments()) { + if (func->argumentRemoved(arg->argumentIndex() + 1)) + continue; + if (!ShibokenGenerator::getDefaultValue(func, arg).isEmpty()) + return true; + } + return false; +} + +AbstractMetaArgumentList OverloadData::getArgumentsWithDefaultValues(const AbstractMetaFunction* func) +{ + AbstractMetaArgumentList args; + foreach (AbstractMetaArgument* arg, func->arguments()) { + if (ShibokenGenerator::getDefaultValue(func, arg).isEmpty() + || func->argumentRemoved(arg->argumentIndex() + 1)) + continue; + args << arg; + } + return args; +} + diff --git a/generator/shiboken/overloaddata.h b/generator/shiboken/overloaddata.h new file mode 100644 index 000000000..a0bd4640c --- /dev/null +++ b/generator/shiboken/overloaddata.h @@ -0,0 +1,147 @@ +/* + * This file is part of the Shiboken Python Bindings Generator project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef OVERLOADDATA_H +#define OVERLOADDATA_H + +#include +#include +#include + +class ShibokenGenerator; + +class OverloadData; +typedef QList OverloadDataList; + +class OverloadData +{ +public: + OverloadData(const AbstractMetaFunctionList& overloads, const ShibokenGenerator* generator); + ~OverloadData(); + + int minArgs() const { return m_headOverloadData->m_minArgs; } + int maxArgs() const { return m_headOverloadData->m_maxArgs; } + int argPos() const { return m_argPos; } + + const AbstractMetaType* argType() const { return m_argType; } + + /// Returns a string list containing all the possible return types (including void) for the current OverloadData. + QStringList returnTypes() const; + + /// Returns true if any of the overloads for the current OverloadData has a return type different from void. + bool hasNonVoidReturnType() const; + + /// Returns true if any of the overloads for the current OverloadData has a varargs argument. + bool hasVarargs() const; + + /// Returns true if any of the overloads for the current OverloadData allows threads when called. + bool hasAllowThread() const; + + /// Returns true if any of the overloads for the current OverloadData is static. + bool hasStaticFunction() const; + + /// Returns true if any of the overloads passed as argument is static. + static bool hasStaticFunction(const AbstractMetaFunctionList& overloads); + + /// Returns true if any of the overloads for the current OverloadData is not static. + bool hasInstanceFunction() const; + + /// Returns true if any of the overloads passed as argument is not static. + static bool hasInstanceFunction(const AbstractMetaFunctionList& overloads); + + /// Returns true if among the overloads for the current OverloadData there are static and non-static methods altogether. + bool hasStaticAndInstanceFunctions() const; + + /// Returns true if among the overloads passed as argument there are static and non-static methods altogether. + static bool hasStaticAndInstanceFunctions(const AbstractMetaFunctionList& overloads); + + const AbstractMetaFunction* referenceFunction() const; + const AbstractMetaArgument* argument(const AbstractMetaFunction* func) const; + OverloadDataList overloadDataOnPosition(int argPos) const; + + bool isHeadOverloadData() const { return this == m_headOverloadData; } + + /// Returns the root OverloadData object that represents all the overloads. + OverloadData* headOverloadData() const { return m_headOverloadData; } + + /// Returns the function that has a default value at the current OverloadData argument position, otherwise returns null. + const AbstractMetaFunction* getFunctionWithDefaultValue() const; + + bool nextArgumentHasDefaultValue() const; + /// Returns the nearest occurrence, including this instance, of an argument with a default value. + OverloadData* findNextArgWithDefault(); + bool isFinalOccurrence(const AbstractMetaFunction* func) const; + + /// Returns the list of overloads removing repeated constant functions (ex.: "foo()" and "foo()const", the second is removed). + QList overloadsWithoutRepetition() const; + const QList& overloads() const { return m_overloads; } + OverloadDataList nextOverloadData() const { return m_nextOverloadData; } + OverloadData* previousOverloadData() const { return m_previousOverloadData; } + + QList invalidArgumentLengths() const; + + static int numberOfRemovedArguments(const AbstractMetaFunction* func, int finalArgPos = -1); + static QPair getMinMaxArguments(const AbstractMetaFunctionList& overloads); + /// Returns true if all overloads have no more than one argument. + static bool isSingleArgument(const AbstractMetaFunctionList& overloads); + + void dumpGraph(QString filename) const; + QString dumpGraph() const; + + bool hasArgumentTypeReplace() const; + QString argumentTypeReplaced() const; + + bool hasArgumentWithDefaultValue() const; + static bool hasArgumentWithDefaultValue(const AbstractMetaFunctionList& overloads); + static bool hasArgumentWithDefaultValue(const AbstractMetaFunction* func); + + /// Returns a list of function arguments which have default values and were not removed. + static AbstractMetaArgumentList getArgumentsWithDefaultValues(const AbstractMetaFunction* func); + +private: + OverloadData(OverloadData* headOverloadData, const AbstractMetaFunction* func, + const AbstractMetaType* argType, int argPos); + + void addOverload(const AbstractMetaFunction* func); + OverloadData* addOverloadData(const AbstractMetaFunction* func, const AbstractMetaArgument* arg); + + void sortNextOverloads(); + + int functionNumber(const AbstractMetaFunction* func) const; + OverloadDataList overloadDataOnPosition(OverloadData* overloadData, int argPos) const; + + int m_minArgs; + int m_maxArgs; + int m_argPos; + const AbstractMetaType* m_argType; + QString m_argTypeReplaced; + QList m_overloads; + + OverloadData* m_headOverloadData; + OverloadDataList m_nextOverloadData; + OverloadData* m_previousOverloadData; + const ShibokenGenerator* m_generator; +}; + + +#endif // OVERLOADDATA_H diff --git a/generator/shiboken/shibokengenerator.cpp b/generator/shiboken/shibokengenerator.cpp new file mode 100644 index 000000000..2ae213dbb --- /dev/null +++ b/generator/shiboken/shibokengenerator.cpp @@ -0,0 +1,2466 @@ +/* + * This file is part of the Shiboken Python Bindings Generator project. + * + * Copyright (C) 2009-2012 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "shibokengenerator.h" +#include "overloaddata.h" +#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 QString resolveScopePrefix(const AbstractMetaClass* scope, const QString& value) +{ + if (!scope) + return QString(); + + QString name; + QStringList parts = scope->qualifiedCppName().split("::", QString::SkipEmptyParts); + for(int i = (parts.size() - 1) ; i >= 0; i--) { + if (!value.startsWith(parts[i] + "::")) + name = parts[i] + "::" + name; + else + name = ""; + } + + return name; +} +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] = "checkType"; + m_typeSystemConvName[TypeSystemIsConvertibleFunction] = "isConvertible"; + m_typeSystemConvName[TypeSystemToCppFunction] = "toCpp"; + m_typeSystemConvName[TypeSystemToPythonFunction] = "toPython"; + m_typeSystemConvRegEx[TypeSystemCheckFunction] = QRegExp(CHECKTYPE_REGEX); + m_typeSystemConvRegEx[TypeSystemIsConvertibleFunction] = QRegExp(ISCONVERTIBLE_REGEX); + m_typeSystemConvRegEx[TypeSystemToPythonFunction] = QRegExp(CONVERTTOPYTHON_REGEX); + m_typeSystemConvRegEx[TypeSystemToCppFunction] = QRegExp(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["__str__"] = QString("0"); + m_tpFuncs["__repr__"] = QString("0"); + m_tpFuncs["__iter__"] = QString("0"); + m_tpFuncs["__next__"] = QString("0"); +} + +void ShibokenGenerator::initPrimitiveTypesCorrespondences() +{ + // Python primitive types names + m_pythonPrimitiveTypeName.clear(); + + // PyBool + m_pythonPrimitiveTypeName["bool"] = "PyBool"; + + // PyInt + m_pythonPrimitiveTypeName["char"] = "SbkChar"; + m_pythonPrimitiveTypeName["signed char"] = "SbkChar"; + m_pythonPrimitiveTypeName["unsigned char"] = "SbkChar"; + m_pythonPrimitiveTypeName["int"] = "PyInt"; + m_pythonPrimitiveTypeName["signed int"] = "PyInt"; + m_pythonPrimitiveTypeName["uint"] = "PyInt"; + m_pythonPrimitiveTypeName["unsigned int"] = "PyInt"; + m_pythonPrimitiveTypeName["short"] = "PyInt"; + m_pythonPrimitiveTypeName["ushort"] = "PyInt"; + m_pythonPrimitiveTypeName["signed short"] = "PyInt"; + m_pythonPrimitiveTypeName["signed short int"] = "PyInt"; + m_pythonPrimitiveTypeName["unsigned short"] = "PyInt"; + m_pythonPrimitiveTypeName["unsigned short int"] = "PyInt"; + m_pythonPrimitiveTypeName["long"] = "PyInt"; + + // PyFloat + m_pythonPrimitiveTypeName["double"] = "PyFloat"; + m_pythonPrimitiveTypeName["float"] = "PyFloat"; + + // PyLong + m_pythonPrimitiveTypeName["unsigned long"] = "PyLong"; + m_pythonPrimitiveTypeName["signed long"] = "PyLong"; + m_pythonPrimitiveTypeName["ulong"] = "PyLong"; + m_pythonPrimitiveTypeName["long long"] = "PyLong"; + m_pythonPrimitiveTypeName["__int64"] = "PyLong"; + m_pythonPrimitiveTypeName["unsigned long long"] = "PyLong"; + m_pythonPrimitiveTypeName["unsigned __int64"] = "PyLong"; + + // Python operators + m_pythonOperators.clear(); + + // call operator + m_pythonOperators["operator()"] = "call"; + + // Arithmetic operators + m_pythonOperators["operator+"] = "add"; + m_pythonOperators["operator-"] = "sub"; + m_pythonOperators["operator*"] = "mul"; + m_pythonOperators["operator/"] = "div"; + m_pythonOperators["operator%"] = "mod"; + + // Inplace arithmetic operators + m_pythonOperators["operator+="] = "iadd"; + m_pythonOperators["operator-="] = "isub"; + m_pythonOperators["operator++"] = "iadd"; + m_pythonOperators["operator--"] = "isub"; + m_pythonOperators["operator*="] = "imul"; + m_pythonOperators["operator/="] = "idiv"; + m_pythonOperators["operator%="] = "imod"; + + // Bitwise operators + m_pythonOperators["operator&"] = "and"; + m_pythonOperators["operator^"] = "xor"; + m_pythonOperators["operator|"] = "or"; + m_pythonOperators["operator<<"] = "lshift"; + m_pythonOperators["operator>>"] = "rshift"; + m_pythonOperators["operator~"] = "invert"; + + // Inplace bitwise operators + m_pythonOperators["operator&="] = "iand"; + m_pythonOperators["operator^="] = "ixor"; + m_pythonOperators["operator|="] = "ior"; + m_pythonOperators["operator<<="] = "ilshift"; + m_pythonOperators["operator>>="] = "irshift"; + + // Comparison operators + m_pythonOperators["operator=="] = "eq"; + m_pythonOperators["operator!="] = "ne"; + m_pythonOperators["operator<"] = "lt"; + m_pythonOperators["operator>"] = "gt"; + m_pythonOperators["operator<="] = "le"; + m_pythonOperators["operator>="] = "ge"; + + // Initialize format units for C++->Python->C++ conversion + m_formatUnits.clear(); + m_formatUnits.insert("char", "b"); + m_formatUnits.insert("unsigned char", "B"); + m_formatUnits.insert("int", "i"); + m_formatUnits.insert("unsigned int", "I"); + m_formatUnits.insert("short", "h"); + m_formatUnits.insert("unsigned short", "H"); + m_formatUnits.insert("long", "l"); + m_formatUnits.insert("unsigned long", "k"); + m_formatUnits.insert("long long", "L"); + m_formatUnits.insert("__int64", "L"); + m_formatUnits.insert("unsigned long long", "K"); + m_formatUnits.insert("unsigned __int64", "K"); + m_formatUnits.insert("double", "d"); + m_formatUnits.insert("float", "f"); +} + +void ShibokenGenerator::initKnownPythonTypes() +{ + m_knownPythonTypes.clear(); + m_knownPythonTypes << "PyBool" << "PyInt" << "PyFloat" << "PyLong"; + m_knownPythonTypes << "PyObject" << "PyString" << "PyBuffer"; + m_knownPythonTypes << "PySequence" << "PyTuple" << "PyList" << "PyDict"; + m_knownPythonTypes << "PyObject*" << "PyObject *" << "PyTupleObject*"; +} + +QString ShibokenGenerator::translateTypeForWrapperMethod(const AbstractMetaType* cType, + const AbstractMetaClass* context, + Options options) const +{ + if (cType->isArray()) + return translateTypeForWrapperMethod(cType->arrayElementType(), context, options) + "[]"; + + 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 +{ + bool result = metaClass->isPolymorphic() || metaClass->hasVirtualDestructor(); + if (avoidProtectedHack()) { + result = result || metaClass->hasProtectedFields() || metaClass->hasProtectedDestructor(); + if (!result && metaClass->hasProtectedFunctions()) { + int protectedFunctions = 0; + int protectedOperators = 0; + foreach (const AbstractMetaFunction* func, metaClass->functions()) { + 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 && !metaClass->isNamespace(); +} + +void ShibokenGenerator::lookForEnumsInClassesNotToBeGenerated(AbstractMetaEnumList& enumList, const AbstractMetaClass* metaClass) +{ + if (!metaClass) + return; + + if (metaClass->typeEntry()->codeGeneration() == TypeEntry::GenerateForSubclass) { + foreach (const AbstractMetaEnum* metaEnum, metaClass->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("::", "_"); + + result +="Wrapper"; + return result; + } else { + return metaClass->qualifiedCppName(); + } +} + +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 + '.'); + } + return funcName; +} + +QString ShibokenGenerator::protectedEnumSurrogateName(const AbstractMetaEnum* metaEnum) +{ + return metaEnum->fullName().replace(".", "_") + "_Surrogate"; +} + +QString ShibokenGenerator::protectedFieldGetterName(const AbstractMetaField* field) +{ + return QString("protected_%1_getter").arg(field->name()); +} + +QString ShibokenGenerator::protectedFieldSetterName(const AbstractMetaField* field) +{ + return QString("protected_%1_setter").arg(field->name()); +} + +QString ShibokenGenerator::cpythonFunctionName(const AbstractMetaFunction* func) +{ + QString result; + + if (func->ownerClass()) { + result = cpythonBaseName(func->ownerClass()->typeEntry()); + if (func->isConstructor() || func->isCopyConstructor()) { + result += "_Init"; + } else { + result += "Func_"; + if (func->isOperatorOverload()) + result += ShibokenGenerator::pythonOperatorFunctionName(func); + else + result += func->name(); + } + } else { + result = "Sbk" + moduleName() + "Module_" + func->name(); + } + + return result; +} + +QString ShibokenGenerator::cpythonMethodDefinitionName(const AbstractMetaFunction* func) +{ + if (!func->ownerClass()) + return QString(); + return QString("%1Method_%2").arg(cpythonBaseName(func->ownerClass()->typeEntry())).arg(func->name()); +} + +QString ShibokenGenerator::cpythonGettersSettersDefinitionName(const AbstractMetaClass* metaClass) +{ + return QString("%1_getsetlist").arg(cpythonBaseName(metaClass)); +} + +QString ShibokenGenerator::cpythonSetattroFunctionName(const AbstractMetaClass* metaClass) +{ + return QString("%1_setattro").arg(cpythonBaseName(metaClass)); +} + + +QString ShibokenGenerator::cpythonGetattroFunctionName(const AbstractMetaClass* metaClass) +{ + return QString("%1_getattro").arg(cpythonBaseName(metaClass)); +} + +QString ShibokenGenerator::cpythonGetterFunctionName(const AbstractMetaField* metaField) +{ + return QString("%1_get_%2").arg(cpythonBaseName(metaField->enclosingClass())).arg(metaField->name()); +} + +QString ShibokenGenerator::cpythonSetterFunctionName(const AbstractMetaField* metaField) +{ + return QString("%1_set_%2").arg(cpythonBaseName(metaField->enclosingClass())).arg(metaField->name()); +} + +static QString cpythonEnumFlagsName(QString moduleName, QString qualifiedCppName) +{ + QString result = QString("Sbk%1_%2").arg(moduleName).arg(qualifiedCppName); + result.replace("::", "_"); + return result; +} + +static QString searchForEnumScope(const AbstractMetaClass* metaClass, const QString& value) +{ + QString enumValueName = value.trimmed(); + + if (!metaClass) + return QString(); + + foreach (const AbstractMetaEnum* metaEnum, metaClass->enums()) { + foreach (const AbstractMetaEnumValue* enumValue, metaEnum->values()) { + if (enumValueName == enumValue->name()) + return metaClass->qualifiedCppName(); + } + } + + return searchForEnumScope(metaClass->enclosingClass(), enumValueName); +} + +/* + * This function uses some heuristics to find out the scope for a given + * argument default value. 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) +{ + QString value = getDefaultValue(func, arg); + if (value.isEmpty()) + return QString(); + + static QRegExp enumValueRegEx("^([A-Za-z_]\\w*)?$"); + QString prefix; + QString suffix; + + if (arg->type()->isEnum()) { + const AbstractMetaEnum* metaEnum = findAbstractMetaEnum(arg->type()); + if (metaEnum) + prefix = resolveScopePrefix(metaEnum->enclosingClass(), value); + } else if (arg->type()->isFlags()) { + static QRegExp numberRegEx("^\\d+$"); // Numbers to flags + if (numberRegEx.exactMatch(value)) { + QString typeName = translateTypeForWrapperMethod(arg->type(), func->implementingClass()); + if (arg->type()->isConstant()) + typeName.remove(0, sizeof("const ") / sizeof(char) - 1); + if (arg->type()->isReference()) + typeName.chop(1); + prefix = typeName + '('; + suffix = ')'; + } + + static QRegExp enumCombinationRegEx("^([A-Za-z_][\\w:]*)\\(([^,\\(\\)]*)\\)$"); // FlagName(EnumItem|EnumItem|...) + if (prefix.isEmpty() && enumCombinationRegEx.indexIn(value) != -1) { + QString flagName = enumCombinationRegEx.cap(1); + QStringList enumItems = enumCombinationRegEx.cap(2).split("|"); + QString scope = searchForEnumScope(func->implementingClass(), enumItems.first()); + if (!scope.isEmpty()) + scope.append("::"); + + QStringList fixedEnumItems; + foreach (const QString& enumItem, enumItems) + fixedEnumItems << QString(scope + enumItem); + + if (!fixedEnumItems.isEmpty()) { + prefix = flagName + '('; + value = fixedEnumItems.join("|"); + suffix = ')'; + } + } + } else if (arg->type()->typeEntry()->isValue()) { + const AbstractMetaClass* metaClass = classes().findClass(arg->type()->typeEntry()); + if (enumValueRegEx.exactMatch(value)) + prefix = resolveScopePrefix(metaClass, value); + } else if (arg->type()->isPrimitive() && arg->type()->name() == "int") { + if (enumValueRegEx.exactMatch(value) && func->implementingClass()) + prefix = resolveScopePrefix(func->implementingClass(), value); + } else if(arg->type()->isPrimitive()) { + static QRegExp unknowArgumentRegEx("^(?:[A-Za-z_][\\w:]*\\()?([A-Za-z_]\\w*)(?:\\))?$"); // [PrimitiveType(] DESIREDNAME [)] + if (unknowArgumentRegEx.indexIn(value) != -1 && func->implementingClass()) { + foreach (const AbstractMetaField* field, func->implementingClass()->fields()) { + if (unknowArgumentRegEx.cap(1).trimmed() == field->name()) { + QString fieldName = field->name(); + if (field->isStatic()) { + prefix = resolveScopePrefix(func->implementingClass(), value); + fieldName.prepend(prefix); + prefix= ""; + } else { + fieldName.prepend(CPP_SELF_VAR "->"); + } + value.replace(unknowArgumentRegEx.cap(1), fieldName); + break; + } + } + } + } + + if (!prefix.isEmpty()) + value.prepend(prefix); + if (!suffix.isEmpty()) + value.append(suffix); + + return value; +} + +QString ShibokenGenerator::cpythonEnumName(const EnumTypeEntry* enumEntry) +{ + return cpythonEnumFlagsName(enumEntry->targetLangPackage().replace(".", "_"), enumEntry->qualifiedCppName()); +} + +QString ShibokenGenerator::cpythonFlagsName(const FlagsTypeEntry* flagsEntry) +{ + return cpythonEnumFlagsName(flagsEntry->targetLangPackage().replace(".", "_"), flagsEntry->originalName()); +} + +QString ShibokenGenerator::cpythonSpecialCastFunctionName(const AbstractMetaClass* metaClass) +{ + return cpythonBaseName(metaClass->typeEntry())+"SpecialCastFunction"; +} + +QString ShibokenGenerator::cpythonWrapperCPtr(const AbstractMetaClass* metaClass, QString argName) +{ + return cpythonWrapperCPtr(metaClass->typeEntry(), argName); +} + +QString ShibokenGenerator::cpythonWrapperCPtr(const AbstractMetaType* metaType, QString argName) +{ + return cpythonWrapperCPtr(metaType->typeEntry(), argName); +} + +QString ShibokenGenerator::cpythonWrapperCPtr(const TypeEntry* type, QString argName) +{ + if (!ShibokenGenerator::isWrapperType(type)) + return QString(); + return QString("((::%1*)Shiboken::Conversions::cppPointer(%2, (SbkObject*)%3))") + .arg(type->qualifiedCppName()) + .arg(cpythonTypeNameExt(type)) + .arg(argName); +} + +QString ShibokenGenerator::getFunctionReturnType(const AbstractMetaFunction* func, Options options) const +{ + if (func->ownerClass() && (func->isConstructor() || func->isCopyConstructor())) + return func->ownerClass()->qualifiedCppName() + '*'; + + 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; + foreach (FunctionModification funcMod, func->modifications()) { + foreach (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'); + foreach (const AbstractMetaArgument* arg, func->arguments()) { + if (func->argumentRemoved(arg->argumentIndex() + 1)) + continue; + + if (!func->typeReplaced(arg->argumentIndex() + 1).isEmpty()) { + result += 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()->isReference()) { + result += objType; + } else if (arg->type()->isPrimitive()) { + const PrimitiveTypeEntry* ptype = (const PrimitiveTypeEntry*) arg->type()->typeEntry(); + if (ptype->basicAliasedTypeEntry()) + ptype = ptype->basicAliasedTypeEntry(); + if (m_formatUnits.contains(ptype->name())) + result += m_formatUnits[ptype->name()]; + else + result += objType; + } else if (isCString(arg->type())) { + result += 'z'; + } else { + QString report; + QTextStream(&report) << "Method: " << func->ownerClass()->qualifiedCppName() + << "::" << func->signature() << " => Arg:" + << arg->name() << "index: " << arg->argumentIndex() + << " - cannot be handled properly. Use an inject-code to fix it!"; + ReportHandler::warning(report); + result += '?'; + } + } + return result; +} + +QString ShibokenGenerator::cpythonBaseName(const AbstractMetaType* type) +{ + if (isCString(type)) + return QString("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->isReference()) { + baseName = "Sbk_" + type->name(); + } else if (type->isPrimitive()) { + const PrimitiveTypeEntry* ptype = (const PrimitiveTypeEntry*) type; + while (ptype->basicAliasedTypeEntry()) + ptype = ptype->basicAliasedTypeEntry(); + if (ptype->targetLangApiName() == ptype->name()) + baseName = m_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 = "PySequence"; + break; + case ContainerTypeEntry::SetContainer: + baseName = "PySet"; + break; + case ContainerTypeEntry::MapContainer: + case ContainerTypeEntry::MultiMapContainer: + case ContainerTypeEntry::HashContainer: + case ContainerTypeEntry::MultiHashContainer: + baseName = "PyDict"; + break; + default: + Q_ASSERT(false); + } + } else { + baseName = "PyObject"; + } + return baseName.replace("::", "_"); +} + +QString ShibokenGenerator::cpythonTypeName(const AbstractMetaClass* metaClass) +{ + return cpythonTypeName(metaClass->typeEntry()); +} + +QString ShibokenGenerator::cpythonTypeName(const TypeEntry* type) +{ + return cpythonBaseName(type) + "_Type"; +} + +QString ShibokenGenerator::cpythonTypeNameExt(const TypeEntry* type) +{ + return cppApiVariableName(type->targetLangPackage()) + '[' + getTypeIndexVariableName(type) + ']'; +} + +QString ShibokenGenerator::converterObject(const AbstractMetaType* type) +{ + if (isCString(type)) + return "Shiboken::Conversions::PrimitiveTypeConverter()"; + if (isVoidPointer(type)) + return "Shiboken::Conversions::PrimitiveTypeConverter()"; + if (type->typeEntry()->isContainer()) + return QString("%1[%2]").arg(convertersVariableName(type->typeEntry()->targetLangPackage())).arg(getTypeIndexVariableName(type)); + + return converterObject(type->typeEntry()); +} + +QString ShibokenGenerator::converterObject(const TypeEntry* type) +{ + if (isCppPrimitive(type)) + return QString("Shiboken::Conversions::PrimitiveTypeConverter<%1>()").arg(type->qualifiedCppName()); + if (isWrapperType(type) || type->isEnum() || type->isFlags()) + return QString("SBK_CONVERTER(%1)").arg(cpythonTypeNameExt(type)); + + /* the typedef'd primitive types case */ + const PrimitiveTypeEntry* pte = reinterpret_cast(type); + if (pte->basicAliasedTypeEntry()) + pte = pte->basicAliasedTypeEntry(); + if (pte->isPrimitive() && !pte->isCppPrimitive() && !pte->customConversion()) + return QString("Shiboken::Conversions::PrimitiveTypeConverter<%1>()").arg(pte->qualifiedCppName()); + + return QString("%1[%2]").arg(convertersVariableName(type->targetLangPackage())).arg(getTypeIndexVariableName(type)); +} + +QString ShibokenGenerator::cpythonTypeNameExt(const AbstractMetaType* type) +{ + return cppApiVariableName(type->typeEntry()->targetLangPackage()) + '[' + getTypeIndexVariableName(type) + ']'; +} + +QString ShibokenGenerator::cpythonOperatorFunctionName(const AbstractMetaFunction* func) +{ + if (!func->isOperatorOverload()) + return QString(); + return QString("Sbk") + func->ownerClass()->name() + + '_' + pythonOperatorFunctionName(func->originalName()); +} + +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) +{ + return typeName.replace(" ", "") + .replace(".", "_") + .replace(",", "_") + .replace("<", "_") + .replace(">", "_") + .replace("::", "_") + .replace("*", "PTR") + .replace("&", "REF"); +} +QString ShibokenGenerator::fixedCppTypeName(const TypeEntry* type, QString typeName) +{ + if (typeName.isEmpty()) + typeName = type->qualifiedCppName(); + if (!(type->codeGeneration() & TypeEntry::GenerateTargetLang)) + typeName.prepend(QString("%1_").arg(type->targetLangPackage())); + return _fixedCppTypeName(typeName); +} + +QString ShibokenGenerator::pythonPrimitiveTypeName(const QString& cppTypeName) +{ + return ShibokenGenerator::m_pythonPrimitiveTypeName.value(cppTypeName, QString()); +} + +QString ShibokenGenerator::pythonPrimitiveTypeName(const PrimitiveTypeEntry* type) +{ + while (type->basicAliasedTypeEntry()) + type = type->basicAliasedTypeEntry(); + return pythonPrimitiveTypeName(type->name()); +} + +QString ShibokenGenerator::pythonOperatorFunctionName(QString cppOpFuncName) +{ + QString value = m_pythonOperators.value(cppOpFuncName); + if (value.isEmpty()) { + ReportHandler::warning("Unknown operator: "+cppOpFuncName); + value = "UNKNOWN_OPERATOR"; + } + value.prepend("__").append("__"); + return value; +} + +QString ShibokenGenerator::pythonOperatorFunctionName(const AbstractMetaFunction* func) +{ + QString op = pythonOperatorFunctionName(func->originalName()); + if (func->arguments().isEmpty()) { + if (op == "__sub__") + op = QString("__neg__"); + else if (op == "__add__") + op = QString("__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, 'r'); + } + return op; +} + +QString ShibokenGenerator::pythonRichCompareOperatorId(QString cppOpFuncName) +{ + return QString("Py_%1").arg(m_pythonOperators.value(cppOpFuncName).toUpper()); +} + +QString ShibokenGenerator::pythonRichCompareOperatorId(const AbstractMetaFunction* func) +{ + return pythonRichCompareOperatorId(func->originalName()); +} + +bool ShibokenGenerator::isNumber(QString cpythonApiName) +{ + return cpythonApiName == "PyInt" + || cpythonApiName == "PyFloat" + || cpythonApiName == "PyLong" + || cpythonApiName == "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) == "PyInt"; +} + +bool ShibokenGenerator::isPyInt(const AbstractMetaType* type) +{ + return isPyInt(type->typeEntry()); +} + +bool ShibokenGenerator::isPairContainer(const AbstractMetaType* type) +{ + return type->isContainer() + && ((ContainerTypeEntry*)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(); +} +bool ShibokenGenerator::isWrapperType(const ComplexTypeEntry* type) +{ + return isObjectType(type) || type->isValue(); +} +bool ShibokenGenerator::isWrapperType(const AbstractMetaType* metaType) +{ + return isObjectType(metaType) + || metaType->typeEntry()->isValue(); +} + +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->isReference() && type->indirections() == 0; +} + +bool ShibokenGenerator::isValueTypeWithCopyConstructorOnly(const AbstractMetaClass* metaClass) +{ + if (!metaClass || !metaClass->typeEntry()->isValue()) + return false; + AbstractMetaFunctionList ctors = metaClass->queryFunctions(AbstractMetaClass::Constructors); + if (ctors.count() != 1) + return false; + return ctors.first()->isCopyConstructor(); +} + +bool ShibokenGenerator::isValueTypeWithCopyConstructorOnly(const TypeEntry* type) const +{ + if (!type || !type->isValue()) + return false; + return isValueTypeWithCopyConstructorOnly(classes().findClass(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->basicAliasedTypeEntry()) + trueType = trueType->basicAliasedTypeEntry(); + return trueType->isPrimitive() && !trueType->isCppPrimitive() && trueType->qualifiedCppName() != "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->basicAliasedTypeEntry()) + trueType = trueType->basicAliasedTypeEntry(); + return trueType->qualifiedCppName() == "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->isReference() && isWrapperType(metaType) && !isPointer(metaType); +} + +bool ShibokenGenerator::visibilityModifiedToPrivate(const AbstractMetaFunction* func) +{ + foreach (FunctionModification mod, func->modifications()) { + 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 "Shiboken::String::check"; + if (isVoidPointer(metaType)) + return "PyObject_Check"; + return cpythonCheckFunction(metaType->typeEntry(), genericNumberType); + } else if (metaType->typeEntry()->isContainer()) { + QString typeCheck = "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().first(); + if (isPointerToWrapperType(type)) + typeCheck += QString("checkSequenceTypes(%1, ").arg(cpythonTypeNameExt(type)); + else if (isWrapperType(type)) + typeCheck += QString("convertibleSequenceTypes((SbkObjectType*)%1, ").arg(cpythonTypeNameExt(type)); + else + typeCheck += QString("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) ? "Pair" : "Dict"; + const AbstractMetaType* firstType = metaType->instantiations().first(); + const AbstractMetaType* secondType = metaType->instantiations().last(); + if (isPointerToWrapperType(firstType) && isPointerToWrapperType(secondType)) { + typeCheck += QString("check%1Types(%2, %3, ").arg(pyType) + .arg(cpythonTypeNameExt(firstType)) + .arg(cpythonTypeNameExt(secondType)); + } else { + typeCheck += QString("convertible%1Types(%2, %3, %4, %5, ").arg(pyType) + .arg(converterObject(firstType)) + .arg(isPointerToWrapperType(firstType) ? "true" : "false") + .arg(converterObject(secondType)) + .arg(isPointerToWrapperType(secondType) ? "true" : "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("SbkObject_TypeCheck(%1, ").arg(cpythonTypeNameExt(type)); + else if (isCppPrimitive(type)) + return QString("%1_Check").arg(pythonPrimitiveTypeName((const PrimitiveTypeEntry*)type)); + QString typeCheck; + if (type->targetLangApiName() == type->name()) + typeCheck = cpythonIsConvertibleFunction(type); + else if (type->targetLangApiName() == "PyUnicode") + typeCheck = "Shiboken::String::check"; + else + typeCheck = QString("%1_Check").arg(type->targetLangApiName()); + return typeCheck; +} + +QString ShibokenGenerator::guessCPythonCheckFunction(const QString& type, AbstractMetaType** metaType) +{ + *metaType = 0; + if (type == "PyTypeObject") + return "PyType_Check"; + + if (type == "PyBuffer") + return "Shiboken::Buffer::checkType"; + + if (type == "str") + return "Shiboken::String::check"; + + *metaType = buildAbstractMetaTypeFromString(type); + if (*metaType && !(*metaType)->typeEntry()->isCustom()) + return QString(); + + return QString("%1_Check").arg(type); +} + +QString ShibokenGenerator::guessCPythonIsConvertible(const QString& type) +{ + if (type == "PyTypeObject") + return "PyType_Check"; + + AbstractMetaType* metaType = buildAbstractMetaTypeFromString(type); + if (metaType && !metaType->typeEntry()->isCustom()) + return cpythonIsConvertibleFunction(metaType); + + return QString("%1_Check").arg(type); +} + +QString ShibokenGenerator::cpythonIsConvertibleFunction(const TypeEntry* type, bool genericNumberType, bool checkExact) +{ + if (isWrapperType(type)) { + QString isConv = (type->isValue() && !isValueTypeWithCopyConstructorOnly(type)) + ? "isPythonToCppValueConvertible" + : "isPythonToCppPointerConvertible"; + return QString("Shiboken::Conversions::%1((SbkObjectType*)%2, ") + .arg(isConv).arg(cpythonTypeNameExt(type)); + } + return QString("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; + } + + if (isWrapperType(metaType)) { + QString isConv; + if (isPointer(metaType) || isValueTypeWithCopyConstructorOnly(metaType)) + isConv = "isPythonToCppPointerConvertible"; + else if (metaType->isReference()) + isConv = "isPythonToCppReferenceConvertible"; + else + isConv = "isPythonToCppValueConvertible"; + return QString("Shiboken::Conversions::%1((SbkObjectType*)%2, ") + .arg(isConv).arg(cpythonTypeNameExt(metaType)); + } + return QString("Shiboken::Conversions::isPythonToCppConvertible(%1, ") + .arg(converterObject(metaType)); +} + +QString ShibokenGenerator::cpythonToCppConversionFunction(const AbstractMetaClass* metaClass) +{ + return QString("Shiboken::Conversions::pythonToCppPointer((SbkObjectType*)%1, ") + .arg(cpythonTypeNameExt(metaClass->typeEntry())); +} +QString ShibokenGenerator::cpythonToCppConversionFunction(const AbstractMetaType* type, const AbstractMetaClass* context) +{ + if (isWrapperType(type)) { + return QString("Shiboken::Conversions::pythonToCpp%1((SbkObjectType*)%2, ") + .arg(isPointer(type) ? "Pointer" : "Copy") + .arg(cpythonTypeNameExt(type)); + } + return QString("Shiboken::Conversions::pythonToCppCopy(%1, ") + .arg(converterObject(type)); +} + +QString ShibokenGenerator::cpythonToPythonConversionFunction(const AbstractMetaType* type, const AbstractMetaClass* context) +{ + if (isWrapperType(type)) { + QString conversion; + if (type->isReference() && !(type->isValue() && type->isConstant()) && !isPointer(type)) + conversion = "reference"; + else if (type->isValue()) + conversion = "copy"; + else + conversion = "pointer"; + return QString("Shiboken::Conversions::%1ToPython((SbkObjectType*)%2, %3") + .arg(conversion).arg(cpythonTypeNameExt(type)).arg(conversion == "pointer" ? "" : "&"); + } + return QString("Shiboken::Conversions::copyToPython(%1, %2") + .arg(converterObject(type)) + .arg((isCString(type) || isVoidPointer(type)) ? "" : "&"); +} + +QString ShibokenGenerator::cpythonToPythonConversionFunction(const AbstractMetaClass* metaClass) +{ + return cpythonToPythonConversionFunction(metaClass->typeEntry()); +} + +QString ShibokenGenerator::cpythonToPythonConversionFunction(const TypeEntry* type) +{ + if (isWrapperType(type)) { + QString conversion; + if (type->isValue()) + conversion = "copy"; + else + conversion = "pointer"; + return QString("Shiboken::Conversions::%1ToPython((SbkObjectType*)%2, %3") + .arg(conversion).arg(cpythonTypeNameExt(type)).arg(conversion == "pointer" ? "" : "&"); + } + + return QString("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('$', '.'); + + if (!(options & Generator::SkipName)) { + arg += " "; + arg += argument->name(); + } + + QList referenceCounts; + referenceCounts = func->referenceCounts(func->implementingClass(), argument->argumentIndex() + 1); + if ((options & Generator::SkipDefaultValues) != Generator::SkipDefaultValues && + !argument->originalDefaultValueExpression().isEmpty()) + { + QString default_value = argument->originalDefaultValueExpression(); + if (default_value == "NULL") + default_value = NULL_VALUE; + + //WORKAROUND: fix this please + if (default_value.startsWith("new ")) + default_value.remove(0, 4); + + arg += " = " + 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; + foreach (AbstractMetaFunction *func, metaClass->functions()) { + //skip signals + if (func->isSignal() || func->isDestructor() + || (func->isModifiedRemoved() && !func->isAbstract() + && (!avoidProtectedHack() || !func->isProtected()))) + continue; + result << func; + } + return result; +} + +ShibokenGenerator::ExtendedConverterData ShibokenGenerator::getExtendedConverters() const +{ + ExtendedConverterData extConvs; + foreach (const AbstractMetaClass* metaClass, classes()) { + // Use only the classes for the current module. + if (!shouldGenerate(metaClass)) + continue; + foreach (AbstractMetaFunction* convOp, metaClass->operatorOverloads(AbstractMetaClass::ConversionOp)) { + // 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; +} + +QList ShibokenGenerator::getPrimitiveCustomConversions() +{ + QList conversions; + foreach (const PrimitiveTypeEntry* type, primitiveTypes()) { + 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 QString funcCall("%CPPSELF.%FUNCTION_NAME"); + int pos = str.indexOf(funcCall); + if (pos == -1) + return QString(); + pos = pos + funcCall.count(); + while (str.at(pos) == ' ' || str.at(pos) == '\t') + ++pos; + if (str.at(pos) == '(') + ++pos; + int begin = pos; + int counter = 1; + while (counter != 0) { + if (str.at(pos) == '(') + ++counter; + else if (str.at(pos) == ')') + --counter; + ++pos; + } + return str.mid(begin, pos-begin-1); +} + +QString ShibokenGenerator::getCodeSnippets(const CodeSnipList& codeSnips, + CodeSnip::Position position, + TypeSystem::Language language) +{ + QString code; + QTextStream c(&code); + foreach (CodeSnip snip, codeSnips) { + if ((position != CodeSnip::Any && 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("%PYTHONTYPEOBJECT", cpythonTypeName(context) + ".super.ht_type"); + code.replace("%TYPE", wrapperName(context)); + code.replace("%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 = QString("%1"CONV_RULE_OUT_VAR_SUFFIX).arg(arg->name()); + else if (argRemoved || (lastArg && arg->argumentIndex() > lastArg->argumentIndex())) + argValue = QString(CPP_ARG_REMOVED"%1").arg(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(PYTHON_ARGS"[%1]").arg(argPos) : PYTHON_ARG; + } else { + argValue = hasConversionRule + ? QString("%1"CONV_RULE_OUT_VAR_SUFFIX).arg(arg->name()) + : QString(CPP_ARG"%1").arg(argPos); + if (isWrapperType(type)) { + if (type->isReference() && !isPointer(type)) + argValue.prepend('*'); + } + } + } + } else { + argValue = arg->name(); + } + if (!argValue.isEmpty()) + argReplacements << ArgumentVarReplacementPair(arg, argValue); + + } + return argReplacements; +} + +void ShibokenGenerator::writeCodeSnips(QTextStream& s, + const CodeSnipList& codeSnips, + CodeSnip::Position 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; +} + +void ShibokenGenerator::writeCodeSnips(QTextStream& s, + const CodeSnipList& codeSnips, + CodeSnip::Position 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("%PYARG_0", PYTHON_RETURN_VAR); + + static QRegExp pyArgsRegex("%PYARG_(\\d+)"); + if (language == TypeSystem::TargetLangCode) { + if (usePyArgs) { + code.replace(pyArgsRegex, PYTHON_ARGS"[\\1-1]"); + } else { + static QRegExp pyArgsRegexCheck("%PYARG_([2-9]+)"); + if (pyArgsRegexCheck.indexIn(code) != -1) { + ReportHandler::warning("Wrong index for %PYARG variable ("+pyArgsRegexCheck.cap(1)+") on "+func->signature()); + return; + } + code.replace("%PYARG_1", PYTHON_ARG); + } + } else { + // Replaces the simplest case of attribution to a + // Python argument on the binding virtual method. + static QRegExp pyArgsAttributionRegex("%PYARG_(\\d+)\\s*=[^=]\\s*([^;]+)"); + code.replace(pyArgsAttributionRegex, "PyTuple_SET_ITEM(" PYTHON_ARGS ", \\1-1, \\2)"); + code.replace(pyArgsRegex, "PyTuple_GET_ITEM(" PYTHON_ARGS ", \\1-1)"); + } + + // Replace %ARG#_TYPE variables. + foreach (const AbstractMetaArgument* arg, func->arguments()) { + QString argTypeVar = QString("%ARG%1_TYPE").arg(arg->argumentIndex() + 1); + QString argTypeVal = arg->type()->cppSignature(); + code.replace(argTypeVar, argTypeVal); + } + + int pos = 0; + static QRegExp cppArgTypeRegexCheck("%ARG(\\d+)_TYPE"); + while ((pos = cppArgTypeRegexCheck.indexIn(code, pos)) != -1) { + ReportHandler::warning("Wrong index for %ARG#_TYPE variable ("+cppArgTypeRegexCheck.cap(1)+") on "+func->signature()); + pos += cppArgTypeRegexCheck.matchedLength(); + } + + // Replace template variable for return variable name. + if (func->isConstructor()) { + code.replace("%0.", QString("%1->").arg("cptr")); + code.replace("%0", "cptr"); + } else if (func->type()) { + QString returnValueOp = isPointerToWrapperType(func->type()) ? "%1->" : "%1."; + if (ShibokenGenerator::isWrapperType(func->type())) + code.replace("%0.", returnValueOp.arg(CPP_RETURN_VAR)); + code.replace("%0", CPP_RETURN_VAR); + } + + // Replace template variable for self Python object. + QString pySelf = (language == TypeSystem::NativeCode) ? "pySelf" : PYTHON_SELF_VAR; + code.replace("%PYSELF", pySelf); + + // Replace template variable for a pointer to C++ of this object. + if (func->implementingClass()) { + QString replacement = func->isStatic() ? "%1::" : "%1->"; + QString cppSelf; + if (func->isStatic()) + cppSelf = func->ownerClass()->qualifiedCppName(); + else if (language == TypeSystem::NativeCode) + cppSelf = "this"; + else + cppSelf = CPP_SELF_VAR; + + // On comparison operator CPP_SELF_VAR is always a reference. + if (func->isComparisonOperator()) + replacement = "%1."; + + if (func->isVirtual() && !func->isAbstract() && (!avoidProtectedHack() || !func->isProtected())) { + QString methodCallArgs = getArgumentsFromMethodCall(code); + if (!methodCallArgs.isNull()) { + if (func->name() == "metaObject") { + QString wrapperClassName = wrapperName(func->ownerClass()); + QString cppSelfVar = avoidProtectedHack() ? QString("%CPPSELF") : QString("reinterpret_cast<%1*>(%CPPSELF)").arg(wrapperClassName); + code.replace(QString("%CPPSELF.%FUNCTION_NAME(%1)").arg(methodCallArgs), + QString("(Shiboken::Object::hasCppWrapper(reinterpret_cast(%1))" + " ? %2->::%3::%FUNCTION_NAME(%4)" + " : %CPPSELF.%FUNCTION_NAME(%4))").arg(pySelf).arg(cppSelfVar).arg(wrapperClassName).arg(methodCallArgs)); + } else { + code.replace(QString("%CPPSELF.%FUNCTION_NAME(%1)").arg(methodCallArgs), + QString("(Shiboken::Object::hasCppWrapper(reinterpret_cast(%1))" + " ? %CPPSELF->::%TYPE::%FUNCTION_NAME(%2)" + " : %CPPSELF.%FUNCTION_NAME(%2))").arg(pySelf).arg(methodCallArgs)); + } + } + } + + code.replace("%CPPSELF.", replacement.arg(cppSelf)); + code.replace("%CPPSELF", cppSelf); + + if (code.indexOf("%BEGIN_ALLOW_THREADS") > -1) { + if (code.count("%BEGIN_ALLOW_THREADS") == code.count("%END_ALLOW_THREADS")) { + code.replace("%BEGIN_ALLOW_THREADS", BEGIN_ALLOW_THREADS); + code.replace("%END_ALLOW_THREADS", END_ALLOW_THREADS); + } else { + ReportHandler::warning("%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("%PYTHONTYPEOBJECT", cpythonTypeName(func->implementingClass()) + ".super.ht_type"); + } else { + code.replace("%PYTHONTYPEOBJECT.", QString("%1->ob_type->").arg(pySelf)); + code.replace("%PYTHONTYPEOBJECT", QString("%1->ob_type").arg(pySelf)); + } + } + + // Replaces template %ARGUMENT_NAMES and %# variables by argument variables and values. + // Replaces template variables %# for individual arguments. + ArgumentVarReplacementList argReplacements = getArgumentReplacement(func, usePyArgs, language, lastArg); + + QStringList args; + foreach (ArgumentVarReplacementPair pair, argReplacements) { + if (pair.second.startsWith(CPP_ARG_REMOVED)) + continue; + args << pair.second; + } + code.replace("%ARGUMENT_NAMES", args.join(", ")); + + foreach (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->isReference() && !isPointer(type)) + replacement.remove(0, 1); + if (type->isReference() || isPointer(type)) + code.replace(QString("%%1.").arg(idx), QString("%1->").arg(replacement)); + } + code.replace(QString("%%1").arg(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("%PYTHON_ARGUMENTS", 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("%PYTHON_METHOD_OVERRIDE", 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()) { + foreach (const AbstractMetaFunction* f, getFunctionOverloads(func->ownerClass(), func->name())) + hasProtectedOverload |= f->isProtected(); + } + + if (func->isProtected() || hasProtectedOverload) { + code.replace("%TYPE::%FUNCTION_NAME", + QString("%1::%2_protected") + .arg(wrapperName(func->ownerClass())) + .arg(func->originalName())); + code.replace("%FUNCTION_NAME", QString("%1_protected").arg(func->originalName())); + } + } + + if (func->isConstructor() && shouldGenerateCppWrapper(func->ownerClass())) + code.replace("%TYPE", wrapperName(func->ownerClass())); + + if (func->ownerClass()) + code.replace("%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 QRegExp expr("\\s*\\*?\\s*[A-Za-z_][A-Za-z_0-9.]*\\s*(?:\\[[^\\[]+\\])*"); + return expr.exactMatch(code.trimmed()); +} + +// 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("::")) + normalized.remove(0, 2); + QString suffix; + while (normalized.endsWith('*') || normalized.endsWith('&')) { + suffix.prepend(normalized.at(normalized.count() - 1)); + normalized.chop(1); + normalized = normalized.trimmed(); + } + return QString("%1 %2").arg(normalized).arg(suffix).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).toAscii(); + 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) +{ + QRegExp& regex = m_typeSystemConvRegEx[converterVariable]; + int pos = 0; + QList replacements; + while ((pos = regex.indexIn(code, pos)) != -1) { + pos += regex.matchedLength(); + QStringList list = regex.capturedTexts(); + QString conversionString = list.first(); + QString conversionTypeName = list.last(); + const AbstractMetaType* conversionType = buildAbstractMetaTypeFromString(conversionTypeName); + if (!conversionType) { + qFatal(qPrintable(QString("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 = pos - list.first().count(); + int start = end; + while (start > 0 && code.at(start) != '\n') + --start; + while (code.at(start).isSpace()) + ++start; + QString varType = code.mid(start, end - start); + conversionString = varType + list.first(); + varType = miniNormalizer(varType); + QString varName = list.at(1).trimmed(); + if (!varType.isEmpty()) { + if (varType != conversionType->cppSignature()) { + qFatal(qPrintable(QString("Types of receiver variable ('%1') and %CONVERTTOCPP type system variable ('%2') differ.") + .arg(varType).arg(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('*')) { + varName.remove(0, 1); + varName = varName.trimmed(); + } else { + prefix = '&'; + } + QString arg = getConverterTypeSystemVariableArgument(code, pos); + conversionString += arg; + c << arg << ", " << prefix << '(' << varName << ')'; + break; + } + case TypeSystemCheckFunction: + conversion = cpythonCheckFunction(conversionType); + if (conversionType->typeEntry()->isPrimitive() && (conversionType->typeEntry()->name() == "PyObject" || !conversion.endsWith(' '))) { + c << '('; + break; + } + case TypeSystemIsConvertibleFunction: + if (conversion.isEmpty()) + conversion = cpythonIsConvertibleFunction(conversionType); + case TypeSystemToPythonFunction: + if (conversion.isEmpty()) + conversion = cpythonToPythonConversionFunction(conversionType); + default: { + QString arg = getConverterTypeSystemVariableArgument(code, pos); + conversionString += arg; + if (converterVariable == TypeSystemToPythonFunction && !isVariable(arg)) { + qFatal(qPrintable(QString("Only variables are acceptable as argument to %%CONVERTTOPYTHON type system variable on code snippet: '%1'") + .arg(code)), NULL); + } + if (conversion.contains("%in")) { + conversion.prepend('('); + conversion.replace("%in", arg); + } else { + c << arg; + } + } + } + replacements.append(qMakePair(conversionString, conversion)); + } + foreach (StringPair rep, replacements) + code.replace(rep.first, rep.second); +} + +bool ShibokenGenerator::injectedCodeUsesCppSelf(const AbstractMetaFunction* func) +{ + CodeSnipList snips = func->injectedCodeSnips(CodeSnip::Any, TypeSystem::TargetLangCode); + foreach (CodeSnip snip, snips) { + if (snip.code().contains("%CPPSELF")) + return true; + } + return false; +} + +bool ShibokenGenerator::injectedCodeUsesPySelf(const AbstractMetaFunction* func) +{ + CodeSnipList snips = func->injectedCodeSnips(CodeSnip::Any, TypeSystem::NativeCode); + foreach (CodeSnip snip, snips) { + if (snip.code().contains("%PYSELF")) + return true; + } + return false; +} + +bool ShibokenGenerator::injectedCodeCallsCppFunction(const AbstractMetaFunction* func) +{ + QString funcCall = QString("%1(").arg(func->originalName()); + QString wrappedCtorCall; + if (func->isConstructor()) { + funcCall.prepend("new "); + wrappedCtorCall = QString("new %1(").arg(wrapperName(func->ownerClass())); + } + CodeSnipList snips = func->injectedCodeSnips(CodeSnip::Any, TypeSystem::TargetLangCode); + foreach (CodeSnip snip, snips) { + if (snip.code().contains("%FUNCTION_NAME(") || snip.code().contains(funcCall) + || (func->isConstructor() + && ((func->ownerClass()->isPolymorphic() && snip.code().contains(wrappedCtorCall)) + || snip.code().contains("new %TYPE("))) + ) + return true; + } + return false; +} + +bool ShibokenGenerator::injectedCodeCallsPythonOverride(const AbstractMetaFunction* func) +{ + static QRegExp overrideCallRegexCheck("PyObject_Call\\s*\\(\\s*%PYTHON_METHOD_OVERRIDE\\s*,"); + CodeSnipList snips = func->injectedCodeSnips(CodeSnip::Any, TypeSystem::NativeCode); + foreach (CodeSnip snip, snips) { + if (overrideCallRegexCheck.indexIn(snip.code()) != -1) + return true; + } + return false; +} + +bool ShibokenGenerator::injectedCodeHasReturnValueAttribution(const AbstractMetaFunction* func, TypeSystem::Language language) +{ + static QRegExp retValAttributionRegexCheck_native("%0\\s*=[^=]\\s*.+"); + static QRegExp retValAttributionRegexCheck_target("%PYARG_0\\s*=[^=]\\s*.+"); + CodeSnipList snips = func->injectedCodeSnips(CodeSnip::Any, language); + foreach (CodeSnip snip, snips) { + if (language == TypeSystem::TargetLangCode) { + if (retValAttributionRegexCheck_target.indexIn(snip.code()) != -1) + return true; + } else { + if (retValAttributionRegexCheck_native.indexIn(snip.code()) != -1) + return true; + } + } + return false; +} + +bool ShibokenGenerator::injectedCodeUsesArgument(const AbstractMetaFunction* func, int argumentIndex) +{ + CodeSnipList snips = func->injectedCodeSnips(CodeSnip::Any); + foreach (CodeSnip snip, snips) { + QString code = snip.code(); + if (code.contains("%ARGUMENT_NAMES")) + return true; + if (code.contains(QString("%%1").arg(argumentIndex + 1))) + 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()); +} + +bool ShibokenGenerator::classNeedsGetattroFunction(const AbstractMetaClass* metaClass) +{ + if (!metaClass) + return false; + foreach (AbstractMetaFunctionList allOverloads, getFunctionGroups(metaClass).values()) { + AbstractMetaFunctionList overloads; + foreach (AbstractMetaFunction* func, allOverloads) { + if (func->isAssignmentOperator() || func->isCastOperator() || func->isModifiedRemoved() + || func->isPrivate() || func->ownerClass() != func->implementingClass() + || func->isConstructor() || func->isOperatorOverload()) + continue; + overloads.append(func); + } + if (overloads.isEmpty()) + continue; + if (OverloadData::hasStaticAndInstanceFunctions(overloads)) + return true; + } + return false; +} + +AbstractMetaFunctionList ShibokenGenerator::getMethodsWithBothStaticAndNonStaticMethods(const AbstractMetaClass* metaClass) +{ + AbstractMetaFunctionList methods; + if (metaClass) { + foreach (AbstractMetaFunctionList allOverloads, getFunctionGroups(metaClass).values()) { + AbstractMetaFunctionList overloads; + foreach (AbstractMetaFunction* func, allOverloads) { + 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.first()); + } + } + return methods; +} + +AbstractMetaClassList ShibokenGenerator::getBaseClasses(const AbstractMetaClass* metaClass) const +{ + AbstractMetaClassList baseClasses; + if (metaClass) { + foreach (QString parent, metaClass->baseClassNames()) { + AbstractMetaClass* clazz = classes().findClass(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); + foreach (AbstractMetaClass* base, 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(".", "_"); + return QString("%1_python.h").arg(result.toLower()); +} + +QString ShibokenGenerator::extendedIsConvertibleFunctionName(const TypeEntry* targetType) const +{ + return QString("ExtendedIsConvertible_%1_%2").arg(targetType->targetLangPackage().replace('.', '_')).arg(targetType->name()); +} + +QString ShibokenGenerator::extendedToCppFunctionName(const TypeEntry* targetType) const +{ + return QString("ExtendedToCpp_%1_%2").arg(targetType->targetLangPackage().replace('.', '_')).arg(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("::")) + typeSignature = typeSignature.mid(2); + + if (m_metaTypeFromStringCache.contains(typeSignature)) + return m_metaTypeFromStringCache.value(typeSignature); + + QString typeString = typeSignature; + bool isConst = typeString.startsWith("const "); + if (isConst) + typeString.remove(0, sizeof("const ") / sizeof(char) - 1); + + int indirections = typeString.count("*"); + while (typeString.endsWith("*")) { + typeString.chop(1); + typeString = typeString.trimmed(); + } + + bool isReference = typeString.endsWith("&"); + if (isReference) { + typeString.chop(1); + typeString = typeString.trimmed(); + } + + if (typeString.startsWith("::")) + typeString.remove(0, 2); + + QString adjustedTypeName = typeString; + QStringList instantiatedTypes; + int lpos = typeString.indexOf('<'); + if (lpos > -1) { + int rpos = typeString.lastIndexOf('>'); + 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) == '<') { + ++depth; + } else if (type.at(i) == '>') { + --depth; + } else if (type.at(i) == ',' && depth == 0) { + instantiatedTypes << type.mid(start, i - start).trimmed(); + start = i + 1; + } + } + instantiatedTypes << type.mid(start).trimmed(); + adjustedTypeName = adjustedTypeName.left(lpos); + } + } + + TypeEntry* typeEntry = TypeDatabase::instance()->findType(adjustedTypeName); + + AbstractMetaType* metaType = 0; + if (typeEntry) { + metaType = new AbstractMetaType(); + metaType->setTypeEntry(typeEntry); + metaType->setIndirections(indirections); + metaType->setReference(isReference); + metaType->setConstant(isConst); + metaType->setTypeUsagePattern(AbstractMetaType::ContainerPattern); + foreach (const QString& instantiation, 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("::")) + typeName = typeName.mid(2); + if (m_metaTypeFromStringCache.contains(typeName)) + return m_metaTypeFromStringCache.value(typeName); + AbstractMetaType* metaType = new AbstractMetaType; + metaType->setTypeEntry(typeEntry); + metaType->setIndirections(0); + metaType->setReference(false); + 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: "; + foreach (AbstractMetaFunction *func, 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() == "operator[]" || func->name() == "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; + foreach (AbstractMetaFunction* func, lst) { + if (isGroupable(func)) + results[func->name()].append(func); + } + return results; +} + +AbstractMetaFunctionList ShibokenGenerator::getFunctionOverloads(const AbstractMetaClass* scope, const QString& functionName) +{ + AbstractMetaFunctionList lst = scope ? scope->functions() : globalFunctions(); + + AbstractMetaFunctionList results; + foreach (AbstractMetaFunction* func, lst) { + if (func->name() != functionName) + continue; + if (isGroupable(func)) + results << func; + } + 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; + foreach (const AbstractMetaFunction* func, overloads) { + int numArgs = 0; + foreach (const AbstractMetaArgument* arg, func->arguments()) { + if (!func->argumentRemoved(arg->argumentIndex() + 1)) + numArgs++; + } + maxArgs = std::max(maxArgs, numArgs); + minArgs = std::min(minArgs, numArgs); + } + return qMakePair(minArgs, maxArgs); +} + +QMap ShibokenGenerator::options() const +{ + QMap opts(Generator::options()); + opts.insert(AVOID_PROTECTED_HACK, "Avoid the use of the '#define protected public' hack."); + opts.insert(PARENT_CTOR_HEURISTIC, "Enable heuristics to detect parent relationship on constructors."); + opts.insert(RETURN_VALUE_HEURISTIC, "Enable heuristics to detect parent relationship on return values (USE WITH CAUTION!)"); + opts.insert(ENABLE_PYSIDE_EXTENSIONS, "Enable PySide extensions, such as support for signal/slots, use this if you are creating a binding for a Qt-based library."); + opts.insert(DISABLE_VERBOSE_ERROR_MESSAGES, "Disable verbose error messages. Turn the python code hard to debug but safe few kB on the generated bindings."); + opts.insert(USE_ISNULL_AS_NB_NONZERO, "If a class have an isNull()const method, it will be used to compute the value of boolean casts"); + return opts; +} + +static void getCode(QStringList& code, const CodeSnipList& codeSnips) +{ + foreach (const CodeSnip& snip, 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; + + foreach (CustomConversion::TargetToNativeConversion* toNative, toCppConversions) + code.append(toNative->conversion()); +} + +bool ShibokenGenerator::doSetup(const QMap& args) +{ + m_useCtorHeuristic = args.contains(PARENT_CTOR_HEURISTIC); + m_usePySideExtensions = args.contains(ENABLE_PYSIDE_EXTENSIONS); + m_userReturnValueHeuristic = args.contains(RETURN_VALUE_HEURISTIC); + m_verboseErrorMessagesDisabled = args.contains(DISABLE_VERBOSE_ERROR_MESSAGES); + m_useIsNullAsNbNonZero = args.contains(USE_ISNULL_AS_NB_NONZERO); + m_avoidProtectedHack = args.contains(AVOID_PROTECTED_HACK); + + TypeDatabase* td = TypeDatabase::instance(); + QStringList snips; + foreach (const PrimitiveTypeEntry* type, primitiveTypes()) + getCode(snips, type); + foreach (const ContainerTypeEntry* type, containerTypes()) + getCode(snips, type); + foreach (const AbstractMetaClass* metaClass, classes()) + getCode(snips, metaClass->typeEntry()); + getCode(snips, td->findType(packageName())); + foreach (AbstractMetaFunctionList globalOverloads, getFunctionGroups().values()) { + foreach (AbstractMetaFunction* func, globalOverloads) + getCode(snips, func->injectedCodeSnips()); + } + + foreach (const QString& code, snips) { + collectContainerTypesFromConverterMacros(code, true); + collectContainerTypesFromConverterMacros(code, false); + } + + return true; +} + +void ShibokenGenerator::collectContainerTypesFromConverterMacros(const QString& code, bool toPythonMacro) +{ + QString convMacro = toPythonMacro ? "%CONVERTTOPYTHON[" : "%CONVERTTOCPP["; + int offset = toPythonMacro ? sizeof("%CONVERTTOPYTHON") : sizeof("%CONVERTTOCPP"); + int start = 0; + while ((start = code.indexOf(convMacro, start)) != -1) { + int end = code.indexOf("]", start); + start += offset; + if (code.at(start) != '%') { + QString typeString = code.mid(start, end - start); + AbstractMetaType* type = buildAbstractMetaTypeFromString(typeString); + addInstantiatedContainers(type); + } + 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(".", "_"); + result.prepend("Sbk"); + result.append("Types"); + return result; +} + +QString ShibokenGenerator::convertersVariableName(const QString& moduleName) const +{ + QString result = cppApiVariableName(moduleName); + result.chop(1); + result.append("Converters"); + return result; +} + +static QString processInstantiationsVariableName(const AbstractMetaType* type) +{ + QString res = QString("_%1").arg(_fixedCppTypeName(type->typeEntry()->qualifiedCppName()).toUpper()); + foreach (const AbstractMetaType* instantiation, type->instantiations()) { + res += instantiation->isContainer() + ? processInstantiationsVariableName(instantiation) + : QString("_%1").arg(_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; + foreach (const AbstractMetaType* instantiation, metaClass->templateBaseClassInstantiations()) + instantiations += processInstantiationsVariableName(instantiation); + return QString("SBK_%1%2_IDX").arg(base).arg(instantiations); + } + return getTypeIndexVariableName(metaClass->typeEntry()); +} +QString ShibokenGenerator::getTypeIndexVariableName(const TypeEntry* type) +{ + if (type->isCppPrimitive()) { + const PrimitiveTypeEntry* trueType = (const PrimitiveTypeEntry*) type; + if (trueType->basicAliasedTypeEntry()) + type = trueType->basicAliasedTypeEntry(); + } + return QString("SBK_%1_IDX").arg(_fixedCppTypeName(type->qualifiedCppName()).toUpper()); +} +QString ShibokenGenerator::getTypeIndexVariableName(const AbstractMetaType* type) +{ + return QString("SBK%1%2_IDX") + .arg(type->typeEntry()->isContainer() ? "_"+moduleName().toUpper() : "") + .arg(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->isReference())) { + flags = ExcludeConst | ExcludeReference; + } + return flags; +} + +QString ShibokenGenerator::getDefaultValue(const AbstractMetaFunction* func, const AbstractMetaArgument* arg) +{ + if (!arg->defaultValueExpression().isEmpty()) + return arg->defaultValueExpression(); + + //Check modifications + foreach(FunctionModification m, func->modifications()) { + foreach(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()) + qFatal(qPrintable(QString(MIN_CTOR_ERROR_MSG).arg(type->cppSignature())), NULL); + 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()) + qFatal(qPrintable(QString(MIN_CTOR_ERROR_MSG).arg(type->qualifiedCppName())), NULL); + s << " = " << ctor; +} + +bool ShibokenGenerator::isCppIntegralPrimitive(const TypeEntry* type) +{ + if (!type->isCppPrimitive()) + return false; + const PrimitiveTypeEntry* trueType = (const PrimitiveTypeEntry*) type; + if (trueType->basicAliasedTypeEntry()) + trueType = trueType->basicAliasedTypeEntry(); + QString typeName = trueType->qualifiedCppName(); + return !typeName.contains("double") && !typeName.contains("float") && !typeName.contains("wchar"); +} +bool ShibokenGenerator::isCppIntegralPrimitive(const AbstractMetaType* type) +{ + return isCppIntegralPrimitive(type->typeEntry()); +} diff --git a/generator/shiboken/shibokengenerator.h b/generator/shiboken/shibokengenerator.h new file mode 100644 index 000000000..4a73cbc9d --- /dev/null +++ b/generator/shiboken/shibokengenerator.h @@ -0,0 +1,536 @@ +/* + * This file is part of the Shiboken Python Bindings Generator project. + * + * Copyright (C) 2009-2012 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef SHIBOKENGENERATOR_H +#define SHIBOKENGENERATOR_H + +#define CONV_RULE_OUT_VAR_SUFFIX "_out" +#define CPP_ARG "cppArg" +#define CPP_ARG0 CPP_ARG"0" +#define CPP_ARG_REMOVED "removed_"CPP_ARG +#define CPP_RETURN_VAR "cppResult" +#define CPP_SELF_VAR "cppSelf" +#define PYTHON_ARG "pyArg" +#define PYTHON_ARGS PYTHON_ARG"s" +#define PYTHON_OVERRIDE_VAR "pyOverride" +#define PYTHON_RETURN_VAR "pyResult" +#define PYTHON_SELF_VAR "self" +#define THREAD_STATE_SAVER_VAR "threadStateSaver" +#define BEGIN_ALLOW_THREADS "PyThreadState* _save = PyEval_SaveThread(); // Py_BEGIN_ALLOW_THREADS" +#define END_ALLOW_THREADS "PyEval_RestoreThread(_save); // Py_END_ALLOW_THREADS" +#define MIN_CTOR_ERROR_MSG "Could not find a minimal constructor for type '%1'. "\ + "This will result in a compilation error." +#define PYTHON_TO_CPP_VAR "pythonToCpp" + +#define CHECKTYPE_REGEX "%CHECKTYPE\\[([^\\[]*)\\]\\(" +#define ISCONVERTIBLE_REGEX "%ISCONVERTIBLE\\[([^\\[]*)\\]\\(" +#define CONVERTTOPYTHON_REGEX "%CONVERTTOPYTHON\\[([^\\[]*)\\]\\(" +#define CONVERTTOCPP_REGEX "(\\*?%?[a-zA-Z_][\\w\\.]*(?:\\[[^\\[^<^>]+\\])*)"\ + "(?:\\s+)=(?:\\s+)%CONVERTTOCPP\\[([^\\[]*)\\]\\(" + +#include +#include + +#include "overloaddata.h" + +class DocParser; + +/** + * Abstract generator that contains common methods used in CppGenerator and HeaderGenerator. + */ +class ShibokenGenerator : public Generator +{ +public: + ShibokenGenerator(); + virtual ~ShibokenGenerator(); + + QString translateTypeForWrapperMethod(const AbstractMetaType* cType, + const AbstractMetaClass* context, Options opt = NoOption) const; + + /** + * Returns a map with all functions grouped, the function name is used as key. + * Example ofg return value: { "foo" -> ["foo(int)", "foo(int, long)], "bar" -> "bar(double)"} + * \param scope Where to search for functions, null means all global functions. + */ + QMap getFunctionGroups(const AbstractMetaClass* scope = 0); + /** + * Returns all overloads for a function named \p functionName. + * \param scope scope used to search for overloads. + * \param functionName the function name. + */ + AbstractMetaFunctionList getFunctionOverloads(const AbstractMetaClass* scope, const QString& functionName); + /** + * Returns the minimun and maximun number of arguments which this function and all overloads + * can accept. Arguments removed by typesystem are considered as well. + */ + QPair getMinMaxArguments(const AbstractMetaFunction* metaFunction); + /** + * Write a function argument in the C++ in the text stream \p s. + * This function just call \code s << argumentString(); \endcode + * \param s text stream used to write the output. + * \param func the current metafunction. + * \param argument metaargument information to be parsed. + * \param options some extra options. + */ + void writeArgument(QTextStream &s, + const AbstractMetaFunction* func, + const AbstractMetaArgument* argument, + Options options = NoOption) const; + /** + * Create a QString in the C++ format to an function argument. + * \param func the current metafunction. + * \param argument metaargument information to be parsed. + * \param options some extra options. + */ + QString argumentString(const AbstractMetaFunction* func, + const AbstractMetaArgument* argument, + Options options = NoOption) const; + + void writeArgumentNames(QTextStream &s, + const AbstractMetaFunction* func, + Options options = NoOption) const; + + /** + * Function used to write the fucntion arguments on the class buffer. + * \param s the class output buffer + * \param func the pointer to metafunction information + * \param count the number of function arguments + * \param options some extra options used during the parser + */ + void writeFunctionArguments(QTextStream &s, + const AbstractMetaFunction* func, + Options options = NoOption) const; + QString functionReturnType(const AbstractMetaFunction* func, Options options = NoOption) const; + + /// Utility function for writeCodeSnips. + typedef QPair ArgumentVarReplacementPair; + typedef QList ArgumentVarReplacementList; + ArgumentVarReplacementList getArgumentReplacement(const AbstractMetaFunction* func, + bool usePyArgs, TypeSystem::Language language, + const AbstractMetaArgument* lastArg); + + /// Write user's custom code snippets at class or module level. + void writeCodeSnips(QTextStream& s, + const CodeSnipList& codeSnips, + CodeSnip::Position position, + TypeSystem::Language language, + const AbstractMetaClass* context = 0); + /// Write user's custom code snippets at function level. + void writeCodeSnips(QTextStream& s, + const CodeSnipList& codeSnips, + CodeSnip::Position position, + TypeSystem::Language language, + const AbstractMetaFunction* func, + const AbstractMetaArgument* lastArg = 0); + + /// Returns a string with the user's custom code snippets that comply with \p position and \p language. + QString getCodeSnippets(const CodeSnipList& codeSnips, CodeSnip::Position position, TypeSystem::Language language); + + /// Replaces variables for the user's custom code at global or class level. + void processCodeSnip(QString& code, const AbstractMetaClass* context = 0); + + /// Replaces the %CONVERTTOPYTHON type system variable. + inline void replaceConvertToPythonTypeSystemVariable(QString& code) + { + replaceConverterTypeSystemVariable(TypeSystemToPythonFunction, code); + } + /// Replaces the %CONVERTTOCPP type system variable. + inline void replaceConvertToCppTypeSystemVariable(QString& code) + { + replaceConverterTypeSystemVariable(TypeSystemToCppFunction, code); + } + /// Replaces the %ISCONVERTIBLE type system variable. + inline void replaceIsConvertibleToCppTypeSystemVariable(QString& code) + { + replaceConverterTypeSystemVariable(TypeSystemIsConvertibleFunction, code); + } + /// Replaces the %CHECKTYPE type system variable. + inline void replaceTypeCheckTypeSystemVariable(QString& code) + { + replaceConverterTypeSystemVariable(TypeSystemCheckFunction, code); + } + + /** + * Verifies if any of the function's code injections of the "target" + * type needs the type system variable "%CPPSELF". + * \param func the function to check + * \return true if the function's target code snippets use "%CPPSELF" + */ + bool injectedCodeUsesCppSelf(const AbstractMetaFunction* func); + + /** + * Verifies if any of the function's code injections of the "native" + * type needs the type system variable "%PYSELF". + * \param func the function to check + * \return true if the function's native code snippets use "%PYSELF" + */ + bool injectedCodeUsesPySelf(const AbstractMetaFunction* func); + + /** + * Verifies if any of the function's code injections makes a call + * to the C++ method. This is used by the generator to avoid writing calls + * to C++ when the user custom code already does this. + * \param func the function to check + * \return true if the function's code snippets call the wrapped C++ function + */ + bool injectedCodeCallsCppFunction(const AbstractMetaFunction* func); + + /** + * Verifies if any of the function's code injections of the "native" class makes a + * call to the C++ method. This is used by the generator to avoid writing calls to + * Python overrides of C++ virtual methods when the user custom code already does this. + * \param func the function to check + * \return true if the function's code snippets call the Python override for a C++ virtual method + */ + bool injectedCodeCallsPythonOverride(const AbstractMetaFunction* func); + + /** + * Verifies if any of the function's code injections attributes values to + * the return variable (%0 or %PYARG_0). + * \param func the function to check + * \param language the kind of code snip + * \return true if the function's code attributes values to "%0" or "%PYARG_0" + */ + bool injectedCodeHasReturnValueAttribution(const AbstractMetaFunction* func, TypeSystem::Language language = TypeSystem::TargetLangCode); + + /** + * Verifies if any of the function's code injections uses the type system variable + * for function arguments of a given index. + */ + bool injectedCodeUsesArgument(const AbstractMetaFunction* func, int argumentIndex); + + /** + * Function which parse the metafunction information + * \param func the function witch will be parserd + * \param option some extra options + * \param arg_count the number of function arguments + */ + QString functionSignature(const AbstractMetaFunction* func, + QString prepend = "", + QString append = "", + Options options = NoOption, + int arg_count = -1) const; + + /// Returns true if there are cases of multiple inheritance in any of its ancestors. + bool hasMultipleInheritanceInAncestry(const AbstractMetaClass* metaClass); + + /// Returns true if the class needs to have a getattro function. + bool classNeedsGetattroFunction(const AbstractMetaClass* metaClass); + + /// Returns a list of methods of the given class where each one is part of a different overload with both static and non-static method. + AbstractMetaFunctionList getMethodsWithBothStaticAndNonStaticMethods(const AbstractMetaClass* metaClass); + + /// Returns a list of parent classes for a given class. + AbstractMetaClassList getBaseClasses(const AbstractMetaClass* metaClass) const; + + /// Returns a list of all ancestor classes for the given class. + AbstractMetaClassList getAllAncestors(const AbstractMetaClass* metaClass) const; + + const AbstractMetaClass* getMultipleInheritingClass(const AbstractMetaClass* metaClass); + + void writeToPythonConversion(QTextStream& s, const AbstractMetaType* type, + const AbstractMetaClass* context, const QString& argumentName); + void writeToCppConversion(QTextStream& s, const AbstractMetaType* type, const AbstractMetaClass* context, const QString& inArgName, const QString& outArgName); + void writeToCppConversion(QTextStream& s, const AbstractMetaClass* metaClass, const QString& inArgName, const QString& outArgName); + + /// Returns true if the argument is a pointer that rejects NULL values. + bool shouldRejectNullPointerArgument(const AbstractMetaFunction* func, int argIndex); + + /// Verifies if the class should have a C++ wrapper generated for it, instead of only a Python wrapper. + bool shouldGenerateCppWrapper(const AbstractMetaClass* metaClass) const; + + /// Adds enums eligible for generation from classes/namespaces marked not to be generated. + static void lookForEnumsInClassesNotToBeGenerated(AbstractMetaEnumList& enumList, const AbstractMetaClass* metaClass); + /// Returns the enclosing class for an enum, or NULL if it should be global. + const AbstractMetaClass* getProperEnclosingClassForEnum(const AbstractMetaEnum* metaEnum); + + QString wrapperName(const AbstractMetaClass* metaClass) const; + + static QString fullPythonFunctionName(const AbstractMetaFunction* func); + static QString protectedEnumSurrogateName(const AbstractMetaEnum* metaEnum); + static QString protectedFieldGetterName(const AbstractMetaField* field); + static QString protectedFieldSetterName(const AbstractMetaField* field); + + static QString pythonPrimitiveTypeName(const QString& cppTypeName); + static QString pythonPrimitiveTypeName(const PrimitiveTypeEntry* type); + + static QString pythonOperatorFunctionName(QString cppOpFuncName); + static QString pythonOperatorFunctionName(const AbstractMetaFunction* func); + static QString pythonRichCompareOperatorId(QString cppOpFuncName); + static QString pythonRichCompareOperatorId(const AbstractMetaFunction* func); + + static QString cpythonOperatorFunctionName(const AbstractMetaFunction* func); + + static QString fixedCppTypeName(const CustomConversion::TargetToNativeConversion* toNative); + static QString fixedCppTypeName(const AbstractMetaType* type); + static QString fixedCppTypeName(const TypeEntry* type, QString typeName = QString()); + + static bool isNumber(QString cpythonApiName); + static bool isNumber(const TypeEntry* type); + static bool isNumber(const AbstractMetaType* type); + static bool isPyInt(const TypeEntry* type); + static bool isPyInt(const AbstractMetaType* type); + static bool isPairContainer(const AbstractMetaType* type); + + /** + * Returns true if the type passed has a Python wrapper for it. + * Although namespace has a Python wrapper, it's not considered a type. + */ + static bool isWrapperType(const TypeEntry* type); + static bool isWrapperType(const ComplexTypeEntry* type); + static bool isWrapperType(const AbstractMetaType* metaType); + + /** + * Checks if the type is an Object/QObject or pointer to Value Type. + * In other words, tells if the type is "T*" and T has a Python wrapper. + */ + static bool isPointerToWrapperType(const AbstractMetaType* type); + + /** + * Returns true if \p type is an Object Type used as a value. + */ + static bool isObjectTypeUsedAsValueType(const AbstractMetaType* type); + + static bool isValueTypeWithCopyConstructorOnly(const AbstractMetaClass* metaClass); + bool isValueTypeWithCopyConstructorOnly(const TypeEntry* type) const; + bool isValueTypeWithCopyConstructorOnly(const AbstractMetaType* type) const; + + /// Returns true if the type is a primitive but not a C++ primitive. + static bool isUserPrimitive(const TypeEntry* type); + static bool isUserPrimitive(const AbstractMetaType* type); + + /// Returns true if the type is a C++ primitive, a void*, a const char*, or a std::string. + static bool isCppPrimitive(const TypeEntry* type); + static bool isCppPrimitive(const AbstractMetaType* type); + + /// Returns true if the type is a C++ integral primitive, i.e. bool, char, int, long, and their unsigned counterparts. + static bool isCppIntegralPrimitive(const TypeEntry* type); + static bool isCppIntegralPrimitive(const AbstractMetaType* type); + + /// Checks if an argument type should be dereferenced by the Python method wrapper before calling the C++ method. + static bool shouldDereferenceArgumentPointer(const AbstractMetaArgument* arg); + /// Checks if a meta type should be dereferenced by the Python method wrapper passing it to C++. + static bool shouldDereferenceAbstractMetaTypePointer(const AbstractMetaType* metaType); + + static bool visibilityModifiedToPrivate(const AbstractMetaFunction* func); + + QString converterObject(const AbstractMetaType* type); + QString converterObject(const TypeEntry* type); + + QString cpythonBaseName(const AbstractMetaClass* metaClass); + QString cpythonBaseName(const TypeEntry* type); + QString cpythonBaseName(const AbstractMetaType* type); + QString cpythonTypeName(const AbstractMetaClass* metaClass); + QString cpythonTypeName(const TypeEntry* type); + QString cpythonTypeNameExt(const TypeEntry* type); + QString cpythonTypeNameExt(const AbstractMetaType* type); + QString cpythonCheckFunction(const TypeEntry* type, bool genericNumberType = false); + QString cpythonCheckFunction(const AbstractMetaType* metaType, bool genericNumberType = false); + /** + * Receives the argument \p type and tries to find the appropriate AbstractMetaType for it + * or a custom type check. + * \param type A string representing the type to be discovered. + * \param metaType A pointer to an AbstractMetaType pointer, to where write a new meta type object + * if one is produced from the \p type string. This object must be deallocated by + * the caller. It will set the target variable to NULL, is \p type is a Python type. + * \return A custom check if \p type is a custom type, or an empty string if \p metaType + * receives an existing type object. + */ + QString guessCPythonCheckFunction(const QString& type, AbstractMetaType** metaType); + QString cpythonIsConvertibleFunction(const TypeEntry* type, bool genericNumberType = false, bool checkExact = false); + QString cpythonIsConvertibleFunction(const AbstractMetaType* metaType, bool genericNumberType = false); + inline QString cpythonIsConvertibleFunction(const AbstractMetaArgument* metaArg, bool genericNumberType = false) + { + return cpythonIsConvertibleFunction(metaArg->type(), genericNumberType); + } + QString guessCPythonIsConvertible(const QString& type); + + QString cpythonToCppConversionFunction(const AbstractMetaClass* metaClass); + QString cpythonToCppConversionFunction(const AbstractMetaType* type, const AbstractMetaClass* context = 0); + QString cpythonToPythonConversionFunction(const AbstractMetaType* type, const AbstractMetaClass* context = 0); + QString cpythonToPythonConversionFunction(const AbstractMetaClass* metaClass); + QString cpythonToPythonConversionFunction(const TypeEntry* type); + + QString cpythonFunctionName(const AbstractMetaFunction* func); + QString cpythonMethodDefinitionName(const AbstractMetaFunction* func); + QString cpythonGettersSettersDefinitionName(const AbstractMetaClass* metaClass); + QString cpythonGetattroFunctionName(const AbstractMetaClass* metaClass); + QString cpythonSetattroFunctionName(const AbstractMetaClass* metaClass); + QString cpythonGetterFunctionName(const AbstractMetaField* metaField); + QString cpythonSetterFunctionName(const AbstractMetaField* metaField); + QString cpythonWrapperCPtr(const AbstractMetaClass* metaClass, QString argName = PYTHON_SELF_VAR); + QString cpythonWrapperCPtr(const AbstractMetaType* metaType, QString argName); + QString cpythonWrapperCPtr(const TypeEntry* type, QString argName); + + /// Guesses the scope to where belongs an argument's default value. + QString guessScopeForDefaultValue(const AbstractMetaFunction* func, const AbstractMetaArgument* arg); + + QString cpythonEnumName(const EnumTypeEntry* enumEntry); + inline QString cpythonEnumName(const AbstractMetaEnum* metaEnum) + { + return cpythonEnumName(metaEnum->typeEntry()); + } + + QString cpythonFlagsName(const FlagsTypeEntry* flagsEntry); + inline QString cpythonFlagsName(const AbstractMetaEnum* metaEnum) + { + FlagsTypeEntry* flags = metaEnum->typeEntry()->flags(); + if (!flags) + return QString(); + return cpythonFlagsName(flags); + } + /// Returns the special cast function name, the function used to proper cast class with multiple inheritance. + QString cpythonSpecialCastFunctionName(const AbstractMetaClass* metaClass); + + QString getFunctionReturnType(const AbstractMetaFunction* func, Options options = NoOption) const; + QString getFormatUnitString(const AbstractMetaFunction* func, bool incRef = false) const; + + /// Returns the file name for the module global header. If no module name is provided the current will be used. + QString getModuleHeaderFileName(const QString& moduleName = QString()) const; + + QString extendedIsConvertibleFunctionName(const TypeEntry* targetType) const; + QString extendedToCppFunctionName(const TypeEntry* targetType) const; + + QMap< QString, QString > options() const; + + /// Returns true if the user enabled the so called "parent constructor heuristic". + bool useCtorHeuristic() const; + /// Returns true if the user enabled the so called "return value heuristic". + bool useReturnValueHeuristic() const; + /// Returns true if the user enabled PySide extensions. + bool usePySideExtensions() const; + /// Returns true if the generator should use the result of isNull()const to compute boolean casts. + bool useIsNullAsNbNonZero() const; + /// Returns true if the generated code should use the "#define protected public" hack. + bool avoidProtectedHack() const; + QString cppApiVariableName(const QString& moduleName = QString()) const; + QString convertersVariableName(const QString& moduleName = QString()) const; + /** + * Returns the type index variable name for a given class. If \p alternativeTemplateName is true + * and the class is a typedef for a template class instantiation, it will return an alternative name + * made of the template class and the instantiation values, or an empty string if the class isn't + * derived from a template class at all. + */ + QString getTypeIndexVariableName(const AbstractMetaClass* metaClass, bool alternativeTemplateName = false); + QString getTypeIndexVariableName(const TypeEntry* type); + QString getTypeIndexVariableName(const AbstractMetaType* type); + + /// Returns true if the user don't want verbose error messages on the generated bindings. + bool verboseErrorMessagesDisabled() const; + + /** + * Builds an AbstractMetaType object from a QString. + * Returns NULL if no type could be built from the string. + * \param typeSignature The string describing the type to be built. + * \return A new AbstractMetaType object that must be deleted by the caller, or a NULL pointer in case of failure. + */ + AbstractMetaType* buildAbstractMetaTypeFromString(QString typeSignature); + + /// Creates an AbstractMetaType object from a TypeEntry. + AbstractMetaType* buildAbstractMetaTypeFromTypeEntry(const TypeEntry* typeEntry); + /// Creates an AbstractMetaType object from an AbstractMetaClass. + AbstractMetaType* buildAbstractMetaTypeFromAbstractMetaClass(const AbstractMetaClass* metaClass); + + void writeMinimalConstructorExpression(QTextStream& s, const AbstractMetaType* type, const QString& defaultCtor = QString()); + void writeMinimalConstructorExpression(QTextStream& s, const TypeEntry* type, const QString& defaultCtor = QString()); + + /** + * Helper function to return the flags to be used by a meta type when + * it needs to write some converter code. + */ + static Options getConverterOptions(const AbstractMetaType* metaType); + + /** + * Helper function to find for argument default value + */ + static QString getDefaultValue(const AbstractMetaFunction* func, const AbstractMetaArgument* arg); +protected: + bool doSetup(const QMap& args); + void collectContainerTypesFromConverterMacros(const QString& code, bool toPythonMacro); + // verify whether the class is copyable + bool isCopyable(const AbstractMetaClass* metaClass); + + bool m_native_jump_table; + static QHash m_pythonPrimitiveTypeName; + static QHash m_pythonOperators; + static QHash m_formatUnits; + static QHash m_tpFuncs; + static QStringList m_knownPythonTypes; + + void clearTpFuncs(); + + const char* name() const { return "Shiboken"; } + + /// Initializes correspondences between primitive and Python types. + static void initPrimitiveTypesCorrespondences(); + /// Initializes a list of Python known type names. + static void initKnownPythonTypes(); + + void writeFunctionCall(QTextStream& s, + const AbstractMetaFunction* metaFunc, + Options options = NoOption) const; + + void writeUnusedVariableCast(QTextStream& s, const QString& variableName); + + AbstractMetaFunctionList filterFunctions(const AbstractMetaClass* metaClass); + + // All data about extended converters: the type entries of the target type, and a + // list of AbstractMetaClasses accepted as argument for the conversion. + typedef QHash > ExtendedConverterData; + /// Returns all extended conversions for the current module. + ExtendedConverterData getExtendedConverters() const; + + /// Returns a list of converters for the non wrapper types of the current module. + QList getPrimitiveCustomConversions(); + + /// Returns true if the Python wrapper for the received OverloadData must accept a list of arguments. + static bool pythonFunctionWrapperUsesListOfArguments(const OverloadData& overloadData); + + Indentor INDENT; + + enum TypeSystemConverterVariable { + TypeSystemCheckFunction = 0, + TypeSystemIsConvertibleFunction, + TypeSystemToCppFunction, + TypeSystemToPythonFunction, + TypeSystemConverterVariables + }; + void replaceConverterTypeSystemVariable(TypeSystemConverterVariable converterVariable, QString& code); + +private: + bool m_useCtorHeuristic; + bool m_userReturnValueHeuristic; + bool m_usePySideExtensions; + bool m_verboseErrorMessagesDisabled; + bool m_useIsNullAsNbNonZero; + bool m_avoidProtectedHack; + + typedef QHash AbstractMetaTypeCache; + AbstractMetaTypeCache m_metaTypeFromStringCache; + + /// Type system converter variable replacement names and regular expressions. + QString m_typeSystemConvName[TypeSystemConverterVariables]; + QRegExp m_typeSystemConvRegEx[TypeSystemConverterVariables]; +}; + +#endif // SHIBOKENGENERATOR_H diff --git a/generator/shiboken/shibokennormalize.cpp b/generator/shiboken/shibokennormalize.cpp new file mode 100644 index 000000000..01aad9732 --- /dev/null +++ b/generator/shiboken/shibokennormalize.cpp @@ -0,0 +1,274 @@ +/* + * This file is part of the PySide project. + * This code was extracted from qmetaobject_p.h present on Qt4.7. + * + * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "shibokennormalize_p.h" +#include + +#if (QT_VERSION < QT_VERSION_CHECK(4, 7, 0)) + +// mirrored in moc's utils.h +static inline bool is_ident_char(char s) +{ + return ((s >= 'a' && s <= 'z') + || (s >= 'A' && s <= 'Z') + || (s >= '0' && s <= '9') + || s == '_' + ); +} + +static inline bool is_space(char s) +{ + return (s == ' ' || s == '\t'); +} + +static void qRemoveWhitespace(const char *s, char *d) +{ + char last = 0; + while (*s && is_space(*s)) + s++; + while (*s) { + while (*s && !is_space(*s)) + last = *d++ = *s++; + while (*s && is_space(*s)) + s++; + if (*s && ((is_ident_char(*s) && is_ident_char(last)) + || ((*s == ':') && (last == '<')))) { + last = *d++ = ' '; + } + } + *d = '\0'; +} + +// This code is shared with moc.cpp +static QByteArray normalizeTypeInternalQt47(const char *t, const char *e, bool fixScope = false, bool adjustConst = true) +{ + int len = e - t; + /* + Convert 'char const *' into 'const char *'. Start at index 1, + not 0, because 'const char *' is already OK. + */ + QByteArray constbuf; + for (int i = 1; i < len; i++) { + if ( t[i] == 'c' + && strncmp(t + i + 1, "onst", 4) == 0 + && (i + 5 >= len || !is_ident_char(t[i + 5])) + && !is_ident_char(t[i-1]) + ) { + constbuf = QByteArray(t, len); + if (is_space(t[i-1])) + constbuf.remove(i-1, 6); + else + constbuf.remove(i, 5); + constbuf.prepend("const "); + t = constbuf.data(); + e = constbuf.data() + constbuf.length(); + break; + } + /* + We musn't convert 'char * const *' into 'const char **' + and we must beware of 'Bar'. + */ + if (t[i] == '&' || t[i] == '*' ||t[i] == '<') + break; + } + if (adjustConst && e > t + 6 && strncmp("const ", t, 6) == 0) { + if (*(e-1) == '&') { // treat const reference as value + t += 6; + --e; + } else if (is_ident_char(*(e-1)) || *(e-1) == '>') { // treat const value as value + t += 6; + } + } + QByteArray result; + result.reserve(len); + +#if 1 + // consume initial 'const ' + if (strncmp("const ", t, 6) == 0) { + t+= 6; + result += "const "; + } +#endif + + // some type substitutions for 'unsigned x' + if (strncmp("unsigned", t, 8) == 0) { + // make sure "unsigned" is an isolated word before making substitutions + if (!t[8] || !is_ident_char(t[8])) { + if (strncmp(" int", t+8, 4) == 0) { + t += 8+4; + result += "uint"; + } else if (strncmp(" long", t+8, 5) == 0) { + if ((strlen(t + 8 + 5) < 4 || strncmp(t + 8 + 5, " int", 4) != 0) // preserve '[unsigned] long int' + && (strlen(t + 8 + 5) < 5 || strncmp(t + 8 + 5, " long", 5) != 0) // preserve '[unsigned] long long' + ) { + t += 8+5; + result += "ulong"; + } + } else if (strncmp(" short", t+8, 6) != 0 // preserve unsigned short + && strncmp(" char", t+8, 5) != 0) { // preserve unsigned char + // treat rest (unsigned) as uint + t += 8; + result += "uint"; + } + } + } else { + // discard 'struct', 'class', and 'enum'; they are optional + // and we don't want them in the normalized signature + struct { + const char *keyword; + int len; + } optional[] = { + { "struct ", 7 }, + { "class ", 6 }, + { "enum ", 5 }, + { 0, 0 } + }; + int i = 0; + do { + if (strncmp(optional[i].keyword, t, optional[i].len) == 0) { + t += optional[i].len; + break; + } + } while (optional[++i].keyword != 0); + } + + bool star = false; + while (t != e) { + char c = *t++; + if (fixScope && c == ':' && *t == ':' ) { + ++t; + c = *t++; + int i = result.size() - 1; + while (i >= 0 && is_ident_char(result.at(i))) + --i; + result.resize(i + 1); + } + star = star || c == '*'; + result += c; + if (c == '<') { + //template recursion + const char* tt = t; + int templdepth = 1; + while (t != e) { + c = *t++; + if (c == '<') + ++templdepth; + if (c == '>') + --templdepth; + if (templdepth == 0 || (templdepth == 1 && c == ',')) { + result += normalizeTypeInternalQt47(tt, t-1, fixScope, false); + result += c; + if (templdepth == 0) { + if (*t == '>') + result += ' '; // avoid >> + break; + } + tt = t; + } + } + } + + // cv qualifers can appear after the type as well + if (!is_ident_char(c) && t != e && (e - t >= 5 && strncmp("const", t, 5) == 0) + && (e - t == 5 || !is_ident_char(t[5]))) { + t += 5; + while (t != e && is_space(*t)) + ++t; + if (adjustConst && t != e && *t == '&') { + // treat const ref as value + ++t; + } else if (adjustConst && !star) { + // treat const as value + } else if (!star) { + // move const to the front (but not if const comes after a *) + result.prepend("const "); + } else { + // keep const after a * + result += "const"; + } + } + } + + return result; +} + +static char *qNormalizeTypeQt47(char *d, int &templdepth, QByteArray &result) +{ + const char *t = d; + while (*d && (templdepth + || (*d != ',' && *d != ')'))) { + if (*d == '<') + ++templdepth; + if (*d == '>') + --templdepth; + ++d; + } + if (strncmp("void", t, d - t) != 0) + result += normalizeTypeInternalQt47(t, d); + + return d; +} + + +QByteArray QMetaObject_normalizedTypeQt47(const char *type) +{ + QByteArray result; + + if (!type || !*type) + return result; + + QVarLengthArray stackbuf(qstrlen(type) + 1); + qRemoveWhitespace(type, stackbuf.data()); + int templdepth = 0; + qNormalizeTypeQt47(stackbuf.data(), templdepth, result); + + return result; +} + +QByteArray QMetaObject_normalizedSignatureQt47(const char *method) +{ + QByteArray result; + if (!method || !*method) + return result; + int len = int(strlen(method)); + QVarLengthArray stackbuf(len + 1); + char *d = stackbuf.data(); + qRemoveWhitespace(method, d); + + result.reserve(len); + + int argdepth = 0; + int templdepth = 0; + while (*d) { + if (argdepth == 1) + d = qNormalizeTypeQt47(d, templdepth, result); + if (*d == '(') + ++argdepth; + if (*d == ')') + --argdepth; + result += *d++; + } + + return result; +} +#endif diff --git a/generator/shiboken/shibokennormalize_p.h b/generator/shiboken/shibokennormalize_p.h new file mode 100644 index 000000000..0a55b5075 --- /dev/null +++ b/generator/shiboken/shibokennormalize_p.h @@ -0,0 +1,41 @@ +/* + * This file is part of the PySide project. + * + * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SHIBOKENNORMALIZE_P_H +#define SHIBOKENNORMALIZE_P_H + +#include +#include + + +#if (QT_VERSION < QT_VERSION_CHECK(4, 7, 0)) + QByteArray QMetaObject_normalizedTypeQt47(const char *type); + QByteArray QMetaObject_normalizedSignatureQt47(const char *type); + + #define SBK_NORMALIZED_TYPE(x) QMetaObject_normalizedTypeQt47(x) + #define SBK_NORMALIZED_SIGNATURE(x) QMetaObject_normalizedSignatureQt47(x) +#else + #define SBK_NORMALIZED_TYPE(x) QMetaObject::normalizedType(x) + #define SBK_NORMALIZED_SIGNATURE(x) QMetaObject::normalizedSignature(x) +#endif + +#endif //SHIBOKENNORMALIZE_P_H diff --git a/generator/shibokenconfig.h.in b/generator/shibokenconfig.h.in new file mode 100644 index 000000000..7d844a940 --- /dev/null +++ b/generator/shibokenconfig.h.in @@ -0,0 +1,7 @@ +#ifndef SHIBOKENCONFIG_H +#define SHIBOKENCONFIG_H + +#define GENERATOR_BINARY "@GENERATORRUNNER_BINARY@" +#define SHIBOKEN_VERSION "@shiboken_VERSION@" + +#endif -- cgit v1.2.3