From 7f5723eac1366e901391fb5771f4a70a45dc6932 Mon Sep 17 00:00:00 2001 From: Hugo Parente Lima Date: Mon, 20 Sep 2010 17:26:09 -0300 Subject: Shiboken generator code moved to the directory generator. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewer: Renato Araújo Luciano Wolf --- CMakeLists.txt | 95 +- cppgenerator.cpp | 3849 ------------------------------------ cppgenerator.h | 201 -- generator/cppgenerator.cpp | 3849 ++++++++++++++++++++++++++++++++++++ generator/cppgenerator.h | 201 ++ generator/headergenerator.cpp | 610 ++++++ generator/headergenerator.h | 58 + generator/main.cpp | 34 + generator/overloaddata.cpp | 788 ++++++++ generator/overloaddata.h | 147 ++ generator/shiboken.cpp | 27 + generator/shibokengenerator.cpp | 1651 ++++++++++++++++ generator/shibokengenerator.h | 364 ++++ headergenerator.cpp | 610 ------ headergenerator.h | 58 - libshiboken/CMakeLists.txt | 8 + main.cpp | 34 - overloaddata.cpp | 788 -------- overloaddata.h | 147 -- shiboken.cpp | 27 - shibokengenerator.cpp | 1651 ---------------- shibokengenerator.h | 364 ---- tests/CMakeLists.txt | 23 + tests/otherbinding/CMakeLists.txt | 2 +- tests/samplebinding/CMakeLists.txt | 2 +- 25 files changed, 7784 insertions(+), 7804 deletions(-) delete mode 100644 cppgenerator.cpp delete mode 100644 cppgenerator.h create mode 100644 generator/cppgenerator.cpp create mode 100644 generator/cppgenerator.h create mode 100644 generator/headergenerator.cpp create mode 100644 generator/headergenerator.h create mode 100644 generator/main.cpp create mode 100644 generator/overloaddata.cpp create mode 100644 generator/overloaddata.h create mode 100644 generator/shiboken.cpp create mode 100644 generator/shibokengenerator.cpp create mode 100644 generator/shibokengenerator.h delete mode 100644 headergenerator.cpp delete mode 100644 headergenerator.h delete mode 100644 main.cpp delete mode 100644 overloaddata.cpp delete mode 100644 overloaddata.h delete mode 100644 shiboken.cpp delete mode 100644 shibokengenerator.cpp delete mode 100644 shibokengenerator.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 8bb2d4646..2830bcba9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,17 +6,21 @@ cmake_minimum_required(VERSION 2.6) set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules/ ${CMAKE_MODULE_PATH}) -find_package(Qt4 4.5.0 REQUIRED) -find_package(ApiExtractor 0.8 REQUIRED) -find_package(GeneratorRunner 0.6.1 REQUIRED) -find_package(PythonLibs REQUIRED) -find_package(PythonInterpWithDebug REQUIRED) +find_package(Qt4 4.5.0) +find_package(ApiExtractor 0.8) +find_package(GeneratorRunner 0.6.1) +find_package(PythonLibs) +find_package(PythonInterpWithDebug) add_definitions(${QT_DEFINITIONS}) +set(shiboken_MAJOR_VERSION "0") +set(shiboken_MINOR_VERSION "5") +set(shiboken_MICRO_VERSION "1") +set(shiboken_VERSION "${shiboken_MAJOR_VERSION}.${shiboken_MINOR_VERSION}.${shiboken_MICRO_VERSION}") + option(AVOID_PROTECTED_HACK "Avoid protected hack on generated bindings." FALSE) option(BUILD_TESTS "Build tests." TRUE) -option(ENABLE_VERSION_SUFFIX "Used to use current version in suffix to generated files. This is used to allow multiples versions installed simultaneous." FALSE) if(MSVC) set(CMAKE_CXX_FLAGS "/Zc:wchar_t- /GR /EHsc /DWIN32 /D_WINDOWS /D_SCL_SECURE_NO_WARNINGS") @@ -35,6 +39,9 @@ else() endif() endif() +set(LIB_SUFFIX "" CACHE STRING "Define suffix of directory name (32/64)" ) +set(LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}" CACHE PATH "The subdirectory relative to the install prefix where libraries will be installed (default is /lib${LIB_SUFFIX})" FORCE) + if (WIN32) set(PATH_SEP "\;") else() @@ -48,45 +55,6 @@ else() message(STATUS "Using protected hack!") endif() -set(LIB_SUFFIX "" CACHE STRING "Define suffix of directory name (32/64)" ) -set(LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}" CACHE PATH "The subdirectory relative to the install prefix where libraries will be installed (default is /lib${LIB_SUFFIX})" FORCE) - -set(shiboken_MAJOR_VERSION "0") -set(shiboken_MINOR_VERSION "5") -set(shiboken_MICRO_VERSION "1") -set(shiboken_VERSION "${shiboken_MAJOR_VERSION}.${shiboken_MINOR_VERSION}.${shiboken_MICRO_VERSION}") -if(ENABLE_VERSION_SUFFIX) - set(shiboken_SUFFIX "-${shiboken_MAJOR_VERSION}.${shiboken_MINOR_VERSION}") -else() - set(shiboken_SUFFIX "") -endif() - -set(shiboken_SRC -cppgenerator.cpp -headergenerator.cpp -overloaddata.cpp -shiboken.cpp -shibokengenerator.cpp -) - -include_directories(${CMAKE_CURRENT_SOURCE_DIR} - ${APIEXTRACTOR_INCLUDE_DIR} - ${GENERATORRUNNER_INCLUDE_DIR} - ${QT_INCLUDE_DIR} - ${QT_QTCORE_INCLUDE_DIR}) - -add_library(shiboken_generator SHARED ${shiboken_SRC}) -set_property(TARGET shiboken_generator PROPERTY PREFIX "") - -target_link_libraries(shiboken_generator - ${APIEXTRACTOR_LIBRARY} - ${GENERATORRUNNER_LIBRARY} - ${QT_QTCORE_LIBRARY}) - -add_executable(shiboken main.cpp) -set_target_properties(shiboken PROPERTIES OUTPUT_NAME shiboken${shiboken_SUFFIX}) -target_link_libraries(shiboken ${QT_QTCORE_LIBRARY}) - # uninstall target configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" @@ -94,28 +62,6 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake" add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") -# Detect if the python libs were compiled in debug mode -execute_process( - COMMAND ${PYTHON_EXECUTABLE} -c "from distutils import sysconfig; \\ - print sysconfig.get_config_var('Py_DEBUG')" - OUTPUT_VARIABLE PY_DEBUG - OUTPUT_STRIP_TRAILING_WHITESPACE) - -if(CMAKE_BUILD_TYPE STREQUAL "Debug") - if(NOT PYTHON_DEBUG_LIBRARIES) - message(FATAL_ERROR "Python debug library not found. Try compile shiboken with -DCMAKE_BUILD_TYPE=Release") - endif() - if(NOT PY_DEBUG) - message(WARNING "Compiling shiboken with debug enabled, but the python executable was not compiled with debug support.") - else() - add_definitions("-DPy_DEBUG") - endif() - set(SBK_PYTHON_LIBRARIES ${PYTHON_DEBUG_LIBRARIES}) -else() - set(SBK_PYTHON_LIBRARIES ${PYTHON_LIBRARIES}) - add_definitions("-DNDEBUG") -endif() - if (BUILD_TESTS) enable_testing() endif() @@ -123,8 +69,15 @@ endif() add_subdirectory(libshiboken) add_subdirectory(doc) -if (BUILD_TESTS) - add_subdirectory(tests) +# deps found, compile the generator. +if (QT4_FOUND AND ApiExtractor_FOUND AND GeneratorRunner_FOUND AND PYTHONLIBS_FOUND AND PYTHONINTERP_FOUND) + add_subdirectory(generator) + + if (BUILD_TESTS) + add_subdirectory(tests) + endif() +else() + message("!! Some dependencies were not found, shiboken generator compilation disabled!") endif() add_subdirectory(data) @@ -138,8 +91,4 @@ add_custom_target(dist echo "Source package created at ${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}.tar.bz2.\n" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) -add_dependencies(shiboken shiboken_generator) - -install(TARGETS shiboken_generator DESTINATION "${GENERATORRUNNER_PLUGIN_DIR}") -install(TARGETS shiboken DESTINATION bin) diff --git a/cppgenerator.cpp b/cppgenerator.cpp deleted file mode 100644 index 2df2bae92..000000000 --- a/cppgenerator.cpp +++ /dev/null @@ -1,3849 +0,0 @@ -/* - * 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 - * - */ - -#include "cppgenerator.h" -#include -#include - -#include -#include -#include - -inline CodeSnipList getConversionRule(TypeSystem::Language lang, const AbstractMetaFunction *function) -{ - CodeSnipList list; - - foreach(AbstractMetaArgument *arg, function->arguments()) { - QString convRule = function->conversionRule(lang, arg->argumentIndex() + 1); - if (!convRule.isEmpty()) { - CodeSnip snip(0, TypeSystem::TargetLangCode); - snip.position = CodeSnip::Beginning; - - convRule.replace("%in", arg->name()); - convRule.replace("%out", arg->name() + "_out"); - - snip.addCode(convRule); - list << snip; - } - - } - return list; -} - -// utiliy functions -inline CodeSnipList getReturnConversionRule(TypeSystem::Language lang, - const AbstractMetaFunction *function, - const QString& inputName, - const QString& outputName) -{ - CodeSnipList list; - - QString convRule = function->conversionRule(lang, 0); - if (!convRule.isEmpty()) { - CodeSnip snip(0, lang); - snip.position = CodeSnip::Beginning; - - convRule.replace("%in", inputName); - convRule.replace("%out", outputName); - - snip.addCode(convRule); - list << snip; - } - - return list; -} - - -CppGenerator::CppGenerator() : m_currentErrorCode(0) -{ - // sequence protocol functions - typedef QPair StrPair; - m_sequenceProtocol.insert("__len__", StrPair("PyObject* self", "Py_ssize_t")); - m_sequenceProtocol.insert("__getitem__", StrPair("PyObject* self, Py_ssize_t _i", "PyObject*")); - m_sequenceProtocol.insert("__setitem__", StrPair("PyObject* self, Py_ssize_t _i, PyObject* _value", "int")); - m_sequenceProtocol.insert("__getslice__", StrPair("PyObject* self, Py_ssize_t _i1, Py_ssize_t _i2", "PyObject*")); - m_sequenceProtocol.insert("__setslice__", StrPair("PyObject* self, Py_ssize_t _i1, Py_ssize_t _i2, PyObject* _value", "int")); - m_sequenceProtocol.insert("__contains__", StrPair("PyObject* self, PyObject* _value", "int")); - m_sequenceProtocol.insert("__concat__", StrPair("PyObject* self, PyObject* _other", "PyObject*")); -} - -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(); -} - -/*! - 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; - -#ifndef AVOID_PROTECTED_HACK - if (!metaClass->isNamespace() && !metaClass->hasPrivateDestructor()) { - s << "//workaround to access protected functions" << endl; - s << "#define protected public" << endl << endl; - } -#endif - - // headers - s << "// default includes" << endl; - s << "#include " << endl; - if (usePySideExtensions()) { - s << "#include " << endl; - s << "#include " << endl; - s << "#include " << endl; - } - - s << "#include \n"; - s << "#include \n"; - if (usePySideExtensions()) { - if (metaClass->isQObject()) { - s << "#include \n"; - s << "#include \n"; - } - } - - // 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; - } - } - - //Extra includes - s << endl << "// Extra includes" << endl; - QList includes = metaClass->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; - - s << "using namespace Shiboken;" << 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, 0, 0, metaClass); - s << endl; - } - - if (shouldGenerateCppWrapper(metaClass)) { - s << "// Native ---------------------------------------------------------" << endl; - s << endl; - - 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); -#ifdef AVOID_PROTECTED_HACK - else if (!metaClass->hasPrivateDestructor() && (func->isVirtual() || func->isAbstract())) -#else - else if (func->isVirtual() || func->isAbstract()) -#endif - writeVirtualMethodNative(s, func); - } - -#ifdef AVOID_PROTECTED_HACK - if (!metaClass->hasPrivateDestructor()) { -#endif - - if (usePySideExtensions() && metaClass->isQObject()) - writeMetaObjectMethod(s, metaClass); - - writeDestructorNative(s, metaClass); - -#ifdef AVOID_PROTECTED_HACK - } -#endif - } - - Indentation indentation(INDENT); - - QString methodsDefinitions; - QTextStream md(&methodsDefinitions); - QString singleMethodDefinitions; - QTextStream smd(&singleMethodDefinitions); - - bool hasComparisonOperator = metaClass->hasComparisonOperatorOverload(); - bool typeAsNumber = metaClass->hasArithmeticOperatorOverload() || metaClass->hasLogicalOperatorOverload() || metaClass->hasBitwiseOperatorOverload(); - - 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()) - overloads.append(func); - } - - if (overloads.isEmpty()) - continue; - - const AbstractMetaFunction* rfunc = overloads.first(); - if (m_sequenceProtocol.contains(rfunc->name())) - continue; - - if (rfunc->isConstructor()) - writeConstructorWrapper(s, overloads); - - if (!rfunc->isConstructor() && !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$"), ""); - - // Write single method definitions - s << singleMethodDefinitions; - - // Write methods definition - s << "static PyMethodDef " << className << "_methods[] = {" << endl; - s << methodsDefinitions << 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 (typeAsNumber) { - 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); - } - - s << "// type has number operators" << endl; - writeTypeAsNumberDefinition(s, metaClass); - } - - if (supportsSequenceProtocol(metaClass)) { - writeSequenceMethods(s, metaClass); - writeTypeAsSequenceDefinition(s, metaClass); - } - - if (hasComparisonOperator) { - 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); - writeObjCopierFunction(s, metaClass); - writeClassDefinition(s, metaClass); - s << endl; - - if (metaClass->isPolymorphic() && metaClass->baseClass()) - writeTypeDiscoveryFunction(s, metaClass); - - - foreach (AbstractMetaEnum* cppEnum, metaClass->enums()) { - if (cppEnum->isAnonymous()) - continue; - - bool hasFlags = cppEnum->typeEntry()->flags(); - if (hasFlags) { - writeFlagsMethods(s, cppEnum); - writeFlagsNumberMethodsDefinition(s, cppEnum); - s << endl; - } - - writeEnumDefinition(s, cppEnum); - - if (hasFlags) { - // Write Enum as Flags definition (at the moment used only by QFlags) - writeFlagsDefinition(s, cppEnum); - s << endl; - } - } - s << endl; - - writeClassRegister(s, metaClass); - - // class inject-code native/end - if (!metaClass->typeEntry()->codeSnips().isEmpty()) { - writeCodeSnips(s, metaClass->typeEntry()->codeSnips(), CodeSnip::End, TypeSystem::NativeCode, 0, 0, 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); - if (usePySideExtensions() && func->ownerClass()->isQObject()) - s << ", m_metaObject(0)"; - - 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; - s << INDENT << "BindingManager::instance().invalidateWrapper(this);" << endl; - s << '}' << endl; -} - -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* type = func->type() ? func->type()->typeEntry() : 0; - - QString prefix = wrapperName(func->ownerClass()) + "::"; - s << functionSignature(func, prefix, "", Generator::SkipDefaultValues|Generator::OriginalTypeDescription) << endl; - s << "{" << endl; - - Indentation indentation(INDENT); - - if (func->isAbstract() && func->isModifiedRemoved()) { - ReportHandler::warning("Pure virtual method \"" + func->ownerClass()->name() + "::" + func->minimalSignature() + "\" must be implement but was completely removed on typesystem."); - s << INDENT << "return"; - if (func->type()) { - s << ' '; - writeMinimalConstructorCallArguments(s, func->type()); - } - s << ';' << endl; - s << '}' << endl << endl; - return; - } - - s << INDENT << "Shiboken::GilState gil;" << endl; - - s << INDENT << "Shiboken::AutoDecRef py_override(BindingManager::instance().getOverride(this, \""; - s << func->name() << "\"));" << endl; - - s << INDENT << "if (py_override.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() << '.' << func->name(); - s << "()' not implemented.\");" << endl; - s << INDENT << "return "; - if (func->type()) { - writeMinimalConstructorCallArguments(s, func->type()); - } - } else { - if (func->allowThread()) { - s << INDENT << "Shiboken::ThreadStateSaver " THREAD_STATE_SAVER_VAR ";" << endl; - s << INDENT << THREAD_STATE_SAVER_VAR ".save();" << endl; - } - - s << INDENT << "return this->" << func->implementingClass()->qualifiedCppName() << "::"; - writeFunctionCall(s, func, Generator::VirtualCall); - } - } - s << ';' << endl; - s << INDENT << '}' << endl << endl; - - CodeSnipList convRules = getConversionRule(TypeSystem::TargetLangCode, func); - if (convRules.size()) - writeCodeSnips(s, convRules, CodeSnip::Beginning, TypeSystem::TargetLangCode, func); - - s << INDENT << "Shiboken::AutoDecRef pyargs("; - - if (func->arguments().isEmpty()) { - 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); - bool convert = arg->type()->isObject() - || arg->type()->isQObject() - || arg->type()->isValue() - || arg->type()->isValuePointer() - || arg->type()->isNativePointer() - || arg->type()->isFlags() - || arg->type()->isEnum() - || arg->type()->isContainer() - || arg->type()->isReference() - || (arg->type()->isPrimitive() - && !m_formatUnits.contains(arg->type()->typeEntry()->name())); - - bool hasConversionRule = !func->conversionRule(TypeSystem::TargetLangCode, arg->argumentIndex() + 1).isEmpty(); - - Indentation indentation(INDENT); - ac << INDENT; - if (convert && !hasConversionRule) - writeToPythonConversion(ac, arg->type(), func->ownerClass()); - - if (hasConversionRule) { - ac << arg->name() << "_out"; - } else { - QString argName = arg->name(); -#ifdef AVOID_PROTECTED_HACK - const AbstractMetaEnum* metaEnum = findAbstractMetaEnum(arg->type()); - if (metaEnum && metaEnum->isProtected()) { - argName.prepend(protectedEnumSurrogateName(metaEnum) + '('); - argName.append(')'); - } -#endif - ac << (convert ? "(" : "") << argName << (convert ? ")" : ""); - } - - 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 invalidadeArg" << argMod.index << " = PyTuple_GET_ITEM(pyargs, " << 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(py_override, pyargs, NULL));" << endl; - if (type) { - if (invalidateReturn) - s << INDENT << "bool invalidadeArg0 = " PYTHON_RETURN_VAR "->ob_refcnt == 1;" << 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 "; - writeMinimalConstructorCallArguments(s, func->type()); - s << ';' << endl; - } - s << INDENT << '}' << endl; - - if (func->type()) { - s << INDENT << "// Check return type" << endl; - s << INDENT << "bool typeIsValid = "; - QString desiredType; - if (func->typeReplaced(0).isEmpty()) { - s << cpythonIsConvertibleFunction(func->type()); - // SbkType would return null when the type is a container. - if (func->type()->typeEntry()->isContainer()) { - desiredType = '"' + reinterpret_cast(func->type()->typeEntry())->typeName() + '"'; - } else { - QString typeName = func->type()->cppSignature(); -#ifdef AVOID_PROTECTED_HACK - const AbstractMetaEnum* metaEnum = findAbstractMetaEnum(func->type()); - if (metaEnum && metaEnum->isProtected()) - typeName = protectedEnumSurrogateName(metaEnum); -#endif - if (func->type()->isPrimitive()) - desiredType = "\"" + func->type()->name() + "\""; - else - desiredType = "SbkType<" + typeName + " >()->tp_name"; - } - } else { - s << guessCPythonCheckFunction(func->typeReplaced(0)); - desiredType = '"' + func->typeReplaced(0) + '"'; - } - s << "(" PYTHON_RETURN_VAR ");" << endl; - if (func->type()->isQObject() || func->type()->isObject() || func->type()->isValuePointer()) - s << INDENT << "typeIsValid = typeIsValid || (" PYTHON_RETURN_VAR " == Py_None);" << endl; - - s << INDENT << "if (!typeIsValid) {" << endl; - { - Indentation indent(INDENT); - s << INDENT << "PyErr_Format(PyExc_TypeError, \"Invalid return value in function %s, expected %s, got %s.\", \"" - << func->ownerClass()->name() << '.' << func->name() << "\", " << desiredType - << ", " PYTHON_RETURN_VAR "->ob_type->tp_name);" << endl; - s << INDENT << "return "; - writeMinimalConstructorCallArguments(s, func->type()); - s << ';' << endl; - } - s << INDENT << "}" << endl; - } - - bool hasConversionRule = !func->conversionRule(TypeSystem::NativeCode, 0).isEmpty(); - if (hasConversionRule) { - CodeSnipList convRule = getReturnConversionRule(TypeSystem::NativeCode, func, "", CPP_RETURN_VAR); - writeCodeSnips(s, convRule, CodeSnip::Any, TypeSystem::NativeCode, func); - } else if (!injectedCodeHasReturnValueAttribution(func, TypeSystem::NativeCode)) { - s << INDENT; -#ifdef AVOID_PROTECTED_HACK - QString enumName; - const AbstractMetaEnum* metaEnum = findAbstractMetaEnum(func->type()); - bool isProtectedEnum = metaEnum && metaEnum->isProtected(); - if (isProtectedEnum) { - enumName = metaEnum->name(); - if (metaEnum->enclosingClass()) - enumName = metaEnum->enclosingClass()->qualifiedCppName() + "::" + enumName; - s << enumName; - } else -#endif - s << translateTypeForWrapperMethod(func->type(), func->implementingClass()); - s << " " CPP_RETURN_VAR "("; -#ifdef AVOID_PROTECTED_HACK - if (isProtectedEnum) - s << enumName << '('; -#endif - writeToCppConversion(s, func->type(), func->implementingClass(), PYTHON_RETURN_VAR); -#ifdef AVOID_PROTECTED_HACK - if (isProtectedEnum) - s << ')'; -#endif - s << ')'; - s << ';' << endl; - } - } - } - - if (invalidateReturn) { - s << INDENT << "if (invalidadeArg0)" << endl; - Indentation indentation(INDENT); - s << INDENT << "BindingManager::instance().invalidateWrapper(" << PYTHON_RETURN_VAR ".object());" << endl; - } - - foreach (FunctionModification funcMod, func->modifications()) { - foreach (ArgumentModification argMod, funcMod.argument_mods) { - if (argMod.resetAfterUse) { - s << INDENT << "if (invalidadeArg" << argMod.index << ")" << endl; - Indentation indentation(INDENT); - s << INDENT << "BindingManager::instance().invalidateWrapper(PyTuple_GET_ITEM(pyargs, "; - s << (argMod.index - 1) << "));" << 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 (type) { - if (!invalidateReturn && (func->type()->isObject() || func->type()->isValuePointer()) ) { - s << INDENT << "if (" << PYTHON_RETURN_VAR << "->ob_refcnt < 2) {" << endl; - { - Indentation indent(INDENT); - s << INDENT << "PyErr_SetString(PyExc_ReferenceError, \"Returning last python reference on virutal function: " - << func->ownerClass()->name() << "." << func->name() << "\");" << endl; - s << INDENT << "PyErr_Print();" << endl; - s << INDENT << "assert(false);" << endl; - } - s << INDENT << "}" << endl; - } - s << INDENT << "return " CPP_RETURN_VAR ";" << endl; - } - - s << '}' << endl << endl; -} - -void CppGenerator::writeMetaObjectMethod(QTextStream& s, const AbstractMetaClass* metaClass) -{ - Indentation indentation(INDENT); - QString wrapperClassName = wrapperName(metaClass); - QString prefix = wrapperClassName + "::"; - s << "const QMetaObject* " << wrapperClassName << "::metaObject() const\n{\n"; - s << INDENT << "if (!m_metaObject) {\n"; - { - Indentation indentation(INDENT); - s << INDENT << "PyObject *pySelf = BindingManager::instance().retrieveWrapper(this);\n" - << INDENT << "void *typeData = Shiboken::getTypeUserData(reinterpret_cast(pySelf));" << endl - << INDENT << "if (!typeData) {" << endl; - { - Indentation indentation2(INDENT); - s << INDENT << "m_metaObject = PySide::DynamicQMetaObject::createBasedOn(pySelf, pySelf->ob_type, &" - << metaClass->qualifiedCppName() << "::staticMetaObject);" << endl - << INDENT << "Shiboken::setTypeUserData(reinterpret_cast(pySelf), m_metaObject, PySide::deleteDynamicQMetaObject);" << endl; - } - s << INDENT << "} else {" << endl; - { - Indentation indentation2(INDENT); - s << INDENT << "m_metaObject = reinterpret_cast(typeData);" << endl; - } - s << INDENT << "}" << endl; - } - s << INDENT << "}" << endl; - s << INDENT << "return m_metaObject;\n"; - s << "}\n\n"; - - s << "int " << wrapperClassName << "::qt_metacall(QMetaObject::Call call, int id, void** args)\n"; - s << "{\n"; - s << INDENT << "int result = " << metaClass->qualifiedCppName() << "::qt_metacall(call, id, args);\n"; - s << INDENT << "return result < 0 ? result : PySide::SignalManager::qt_metacall(this, call, id, args);\n"; - s << "}\n\n"; -} - -void CppGenerator::writeConstructorWrapper(QTextStream& s, const AbstractMetaFunctionList overloads) -{ - OverloadData overloadData(overloads, this); - - const AbstractMetaFunction* rfunc = overloadData.referenceFunction(); - const AbstractMetaClass* metaClass = rfunc->ownerClass(); - QString className = cpythonTypeName(metaClass); - - m_currentErrorCode = -1; - - s << "static int" << endl; - s << cpythonFunctionName(rfunc) << "(PyObject* self, PyObject* args, PyObject* kwds)" << endl; - s << '{' << endl; - - // Check if the right constructor was called. - if (!metaClass->hasPrivateDestructor()) { - s << INDENT << "if (Shiboken::isUserType(self) && !Shiboken::canCallConstructor(self->ob_type, Shiboken::SbkType<" << metaClass->qualifiedCppName() << " >()))" << endl; - Indentation indent(INDENT); - s << INDENT << "return " << m_currentErrorCode << ';' << endl << endl; - } - - s << INDENT; - bool hasCppWrapper = shouldGenerateCppWrapper(metaClass); - s << (hasCppWrapper ? wrapperName(metaClass) : metaClass->qualifiedCppName()); - s << "* cptr = 0;" << endl; - - bool needsOverloadId = overloadData.maxArgs() > 0; - if (needsOverloadId) - s << INDENT << "int overloadId = -1;" << 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 << "SbkBaseWrapper* sbkSelf = reinterpret_cast(self);" << endl; - - if (metaClass->isAbstract() || metaClass->baseClassNames().size() > 1) { - s << INDENT << "SbkBaseWrapperType* type = reinterpret_cast(self->ob_type);" << endl; - s << INDENT << "SbkBaseWrapperType* 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 << "type->mi_init = myType->mi_init;" << endl; - s << INDENT << "type->mi_offsets = myType->mi_offsets;" << endl; - s << INDENT << "type->mi_specialcast = myType->mi_specialcast;" << endl; - } - if (!metaClass->isAbstract()) - s << INDENT << '}' << endl << endl; - } - - s << endl; - - if (!metaClass->isQObject() && overloadData.hasArgumentWithDefaultValue()) - s << INDENT << "int numNamedArgs = (kwds ? PyDict_Size(kwds) : 0);" << endl; - if (overloadData.maxArgs() > 0) { - s << endl << INDENT << "int numArgs = "; - writeArgumentsInitializer(s, overloadData); - } - - if (needsOverloadId) - writeOverloadedFunctionDecisor(s, overloadData); - - writeFunctionCalls(s, overloadData); - s << endl; - - s << INDENT << "if (PyErr_Occurred() || !Shiboken::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 << "sbkSelf->validCppObject = 1;" << 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 << "sbkSelf->containsCppWrapper = 1;" << endl; - s << INDENT << "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::signalUpdateSource(self);" << endl; - s << INDENT << "metaObject = cptr->metaObject(); // <- init python qt properties" << endl; - s << INDENT << "if (kwds && !PySide::fillQtProperties(self, metaObject, kwds, argNames, " << argNamesSet.count() << "))" << endl; - { - Indentation indentation(INDENT); - s << INDENT << "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; - m_currentErrorCode = 0; -} - -void CppGenerator::writeMinimalConstructorCallArguments(QTextStream& s, const AbstractMetaClass* metaClass) -{ - if (!metaClass) - return; - - AbstractMetaFunctionList ctors = metaClass->queryFunctions(AbstractMetaClass::Constructors); - const AbstractMetaFunction* ctor = 0; - - foreach (const AbstractMetaFunction* candidate, ctors) { - if (candidate->arguments().size() == 0) { - ctor = candidate; - break; - } - - bool allPrimitives = true; - foreach (const AbstractMetaArgument* arg, candidate->arguments()) { - if (!arg->type()->isPrimitive() && arg->defaultValueExpression().isEmpty()) { - allPrimitives = false; - break; - } - } - if (allPrimitives) { - if (!ctor || candidate->arguments().size() < ctor->arguments().size()) - ctor = candidate; - } - } - - if (!ctor) { - ReportHandler::warning("Class "+metaClass->name()+" does not have a default constructor."); - return; - } - - QStringList argValues; - AbstractMetaArgumentList args = ctor->arguments(); - for (int i = 0; i < args.size(); i++) { - if (args[i]->defaultValueExpression().isEmpty()) - argValues << args[i]->type()->name()+"(0)"; - } - s << metaClass->qualifiedCppName() << '(' << argValues.join(QLatin1String(", ")) << ')'; -} - -void CppGenerator::writeMinimalConstructorCallArguments(QTextStream& s, const AbstractMetaType* metaType) -{ - Q_ASSERT(metaType); - const TypeEntry* type = metaType->typeEntry(); - - if (type->isObject() || metaType->isValuePointer()) { - s << "0"; - } else if (type->isPrimitive()) { - const PrimitiveTypeEntry* primitiveTypeEntry = reinterpret_cast(type); - if (primitiveTypeEntry->hasDefaultConstructor()) - s << primitiveTypeEntry->defaultConstructor(); - else - s << type->name() << "(0)"; - } else if (type->isContainer() || type->isFlags() || type->isEnum()){ - s << metaType->cppSignature() << "()"; - } else if (metaType->isNativePointer() && type->isVoid()) { - s << "0"; - } else { - // this is slowwwww, FIXME: Fix the API od APIExtractor, these things should be easy! - foreach (AbstractMetaClass* metaClass, classes()) { - if (metaClass->typeEntry() == type) { - writeMinimalConstructorCallArguments(s, metaClass); - return; - } - } - ReportHandler::warning("Could not find a AbstractMetaClass for type "+metaType->name()); - } -} - -void CppGenerator::writeMethodWrapper(QTextStream& s, const AbstractMetaFunctionList overloads) -{ - OverloadData overloadData(overloads, this); - const AbstractMetaFunction* rfunc = overloadData.referenceFunction(); - - //DEBUG -// if (rfunc->name() == "operator+" && rfunc->ownerClass()->name() == "Str") { -// QString dumpFile = QString("/tmp/%1_%2.dot").arg(moduleName()).arg(pythonOperatorFunctionName(rfunc)).toLower(); -// overloadData.dumpGraph(dumpFile); -// } - //DEBUG - - int minArgs = overloadData.minArgs(); - int maxArgs = overloadData.maxArgs(); - bool usePyArgs = pythonFunctionWrapperUsesListOfArguments(overloadData); - bool usesNamedArguments = overloadData.hasArgumentWithDefaultValue(); - - s << "static PyObject* "; - s << cpythonFunctionName(rfunc) << "(PyObject* self"; - if (maxArgs > 0) { - s << ", PyObject* arg"; - if (usePyArgs) - s << 's'; - if (usesNamedArguments) - s << ", PyObject* kwds"; - } - s << ')' << endl << '{' << endl; - - if (rfunc->implementingClass() && - (!rfunc->implementingClass()->isNamespace() && overloadData.hasInstanceFunction())) { - - s << INDENT; -#ifdef AVOID_PROTECTED_HACK - QString _wrapperName = wrapperName(rfunc->ownerClass()); - bool hasProtectedMembers = rfunc->ownerClass()->hasProtectedMembers(); - s << (hasProtectedMembers ? _wrapperName : rfunc->ownerClass()->qualifiedCppName()); -#else - s << rfunc->ownerClass()->qualifiedCppName(); -#endif - s << "* " CPP_SELF_VAR " = 0;" << endl; - - if (rfunc->isOperatorOverload() && rfunc->isBinaryOperator()) { - QString checkFunc = cpythonCheckFunction(rfunc->ownerClass()->typeEntry()); - s << INDENT << "bool isReverse = " << checkFunc << "(arg) && !" << checkFunc << "(self);\n" - << INDENT << "if (isReverse)\n"; - Indentation indent(INDENT); - s << INDENT << "std::swap(self, arg);\n\n"; - } - - // Sets the C++ "self" (the "this" for the object) if it has one. - QString cppSelfAttribution = CPP_SELF_VAR " = "; -#ifdef AVOID_PROTECTED_HACK - cppSelfAttribution += (hasProtectedMembers ? QString("(%1*)").arg(_wrapperName) : ""); -#endif - cppSelfAttribution += cpythonWrapperCPtr(rfunc->ownerClass(), "self"); - - // Checks if the underlying C++ object is valid. - if (overloadData.hasStaticFunction()) { - s << INDENT << "if (self) {" << endl; - { - Indentation indent(INDENT); - writeInvalidCppObjectCheck(s); - s << INDENT << cppSelfAttribution << ';' << endl; - } - s << INDENT << '}' << endl; - } else { - writeInvalidCppObjectCheck(s); - s << INDENT << cppSelfAttribution << ';' << endl; - } - s << endl; - } - - bool hasReturnValue = overloadData.hasNonVoidReturnType(); - - if (hasReturnValue && !rfunc->isInplaceOperator()) - s << INDENT << "PyObject* " PYTHON_RETURN_VAR " = 0;" << endl; - - bool needsOverloadId = overloadData.maxArgs() > 0; - if (needsOverloadId) - s << INDENT << "int overloadId = -1;" << endl; - - if (usesNamedArguments) { - s << INDENT << "int numNamedArgs = (kwds ? PyDict_Size(kwds) : 0);" << endl; - } - - if (minArgs != maxArgs || maxArgs > 1) { - s << INDENT << "int numArgs = "; - if (minArgs == 0 && maxArgs == 1 && !usePyArgs) - s << "(arg == 0 ? 0 : 1);" << endl; - else - writeArgumentsInitializer(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 callExtendedReverseOperator = hasReturnValue && !rfunc->isInplaceOperator() && 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 << "&& SbkBaseWrapper_Check(arg)" << endl; - s << INDENT << "&& !PyObject_TypeCheck(arg, self->ob_type)" << endl; - s << INDENT << "&& PyObject_HasAttrString(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(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\"), self);" << 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 (needsOverloadId) - writeOverloadedFunctionDecisor(s, overloadData); - - writeFunctionCalls(s, overloadData); - s << endl; - - if (callExtendedReverseOperator) - s << endl << INDENT << "} // End of \"if (!" PYTHON_RETURN_VAR ")\"" << endl << endl; - - s << endl << INDENT << "if (PyErr_Occurred()"; - if (hasReturnValue && !rfunc->isInplaceOperator()) - s << " || !" PYTHON_RETURN_VAR; - s << ") {" << endl; - { - Indentation indent(INDENT); - if (hasReturnValue && !rfunc->isInplaceOperator()) - s << INDENT << "Py_XDECREF(" PYTHON_RETURN_VAR ");" << endl; - s << INDENT << "return " << m_currentErrorCode << ';' << endl; - } - s << INDENT << '}' << endl; - - if (hasReturnValue) { - if (rfunc->isInplaceOperator()) { - s << INDENT << "Py_INCREF(self);\n"; - s << INDENT << "return self;\n"; - } else { - s << INDENT << "return " PYTHON_RETURN_VAR ";\n"; - } - } else { - s << INDENT << "Py_RETURN_NONE;" << 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(); - - QStringList palist; - - s << INDENT << "PyObject* "; - if (!pythonFunctionWrapperUsesListOfArguments(overloadData)) { - s << "arg = 0"; - palist << "&arg"; - } else { - s << "pyargs[] = {" << QString(maxArgs, '0').split("", QString::SkipEmptyParts).join(", ") << '}'; - for (int i = 0; i < maxArgs; i++) - palist << QString("&(pyargs[%1])").arg(i); - } - s << ';' << endl << endl; - - QString pyargs = palist.join(", "); - - 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 << "pyargs[" << maxArgs << "] = PyTuple_GetSlice(args, " << maxArgs << ", numArgs);" << endl; - s << INDENT << "Shiboken::AutoDecRef auto_varargs(pyargs[" << 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(); - - if (usesNamedArguments) { - s << INDENT << "if (!PyArg_ParseTuple(" << (overloadData.hasVarargs() ? "nonvarargs" : "args"); - s << ", \"|" << QByteArray(maxArgs, 'O') << ':' << funcName << "\", " << pyargs << "))" << endl; - } else { - s << INDENT << "if (!PyArg_UnpackTuple(" << (overloadData.hasVarargs() ? "nonvarargs" : "args"); - s << ", \"" << funcName << "\", " << minArgs << ", " << maxArgs << ", " << pyargs << "))" << endl; - } - { - Indentation indent(INDENT); - s << INDENT << "return " << m_currentErrorCode << ';' << endl; - } - s << endl; -} - -void CppGenerator::writeCppSelfDefinition(QTextStream& s, const AbstractMetaFunction* func) -{ - if (!func->ownerClass() || func->isStatic() || func->isConstructor()) - return; - - s << INDENT; -#ifdef AVOID_PROTECTED_HACK - QString _wrapperName = wrapperName(func->ownerClass()); - bool hasProtectedMembers = func->ownerClass()->hasProtectedMembers(); - s << (hasProtectedMembers ? _wrapperName : func->ownerClass()->qualifiedCppName()) << "* " CPP_SELF_VAR " = "; - s << (hasProtectedMembers ? QString("(%1*)").arg(_wrapperName) : ""); -#else - s << func->ownerClass()->qualifiedCppName() << "* " CPP_SELF_VAR " = "; -#endif - s << cpythonWrapperCPtr(func->ownerClass(), "self") << ';' << endl; - if (func->isUserAdded()) - s << INDENT << "(void)" CPP_SELF_VAR "; // avoid warnings about unused variables" << endl; -} - -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" : "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 = "str"; - } else if (argType->isPrimitive()) { - const PrimitiveTypeEntry* ptp = reinterpret_cast(argType->typeEntry()); - while (ptp->aliasedTypeEntry()) - ptp = ptp->aliasedTypeEntry(); - - 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 (!arg->defaultValueExpression().isEmpty()) { - strArg += " = "; - if ((isCString(argType) || argType->isValuePointer() || argType->typeEntry()->isObject()) - && 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::writeInvalidCppObjectCheck(QTextStream& s, QString pyArgName, const TypeEntry* type) -{ - s << INDENT << "if (Shiboken::cppObjectIsInvalid(" << pyArgName << "))" << endl; - Indentation indent(INDENT); - s << INDENT << "return " << m_currentErrorCode << ';' << endl; -} - -void CppGenerator::writeTypeCheck(QTextStream& s, const AbstractMetaType* argType, QString argumentName, bool isNumber, QString customType) -{ - if (!customType.isEmpty()) - s << guessCPythonCheckFunction(customType); - else if (argType->isEnum()) - s << cpythonIsConvertibleFunction(argType, false); - else - s << cpythonIsConvertibleFunction(argType, isNumber); - - s << '(' << argumentName << ')'; -} - -void CppGenerator::writeTypeCheck(QTextStream& s, const OverloadData* overloadData, QString argumentName) -{ - QSet numericTypes; - - foreach (OverloadData* od, overloadData->previousOverloadData()->nextOverloadData()) { - foreach (const AbstractMetaFunction* func, od->overloads()) { - const AbstractMetaArgument* arg = od->argument(func); - - if (!arg->type()->isPrimitive()) - continue; - if (ShibokenGenerator::isNumber(arg->type()->typeEntry())) - numericTypes << arg->type()->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() : ""); - writeTypeCheck(s, argType, argumentName, numberType, customType); -} - -void CppGenerator::writeArgumentConversion(QTextStream& s, - const AbstractMetaType* argType, - QString argName, QString pyArgName, - const AbstractMetaClass* context, - QString defaultValue) -{ - const TypeEntry* type = argType->typeEntry(); - - if (type->isCustom() || type->isVarargs()) - return; - - QString typeName; - QString baseTypeName = type->name(); - bool isWrappedCppClass = type->isValue() || type->isObject(); - if (isWrappedCppClass) - typeName = baseTypeName + '*'; - else - typeName = translateTypeForWrapperMethod(argType, context); - - if (type->isContainer() || type->isPrimitive()) { - // If the type is a const char*, we don't remove the "const". - if (typeName.startsWith("const ") && !(isCString(argType))) - typeName.remove(0, sizeof("const ") / sizeof(char) - 1); - if (typeName.endsWith("&")) - typeName.chop(1); - } - typeName = typeName.trimmed(); - - bool hasImplicitConversions = !implicitConversions(argType).isEmpty(); - - if (isWrappedCppClass) { - const TypeEntry* typeEntry = (hasImplicitConversions ? type : 0); - writeInvalidCppObjectCheck(s, pyArgName, typeEntry); - } - - // Auto pointer to dealloc new objects created because to satisfy implicit conversion. - if (hasImplicitConversions) - s << INDENT << "std::auto_ptr<" << baseTypeName << " > " << argName << "_auto_ptr;" << endl; - - // Value type that has default value. - if (argType->isValue() && !defaultValue.isEmpty()) - s << INDENT << baseTypeName << ' ' << argName << "_tmp = " << defaultValue << ';' << endl; - - if (usePySideExtensions() && typeName == "QStringRef") { - s << INDENT << "QString " << argName << "_qstring = "; - if (!defaultValue.isEmpty()) - s << pyArgName << " ? "; - s << "Shiboken::Converter::toCpp(" << pyArgName << ')' << endl; - if (!defaultValue.isEmpty()) - s << " : " << defaultValue; - s << ';' << endl; - s << INDENT << "QStringRef " << argName << "(&" << argName << "_qstring);" << endl; - } else { - s << INDENT << typeName << ' ' << argName << " = "; - if (!defaultValue.isEmpty()) - s << pyArgName << " ? "; - s << "Shiboken::Converter<" << typeName << " >::toCpp(" << pyArgName << ')'; - if (!defaultValue.isEmpty()) { - s << " : "; - if (argType->isValue()) - s << '&' << argName << "_tmp"; - else - s << defaultValue; - } - s << ';' << endl; - } - - if (hasImplicitConversions) { - s << INDENT << "if ("; - if (!defaultValue.isEmpty()) - s << pyArgName << " && "; - s << '!' << cpythonCheckFunction(type) << '(' << pyArgName << "))"; - s << endl; - Indentation indent(INDENT); - s << INDENT << argName << "_auto_ptr = std::auto_ptr<" << baseTypeName; - s << " >(" << argName << ");" << endl; - } -} - -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; - 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; - - 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(); - - if (isFirst) { - isFirst = false; - s << INDENT; - } else { - s << " else "; - } - - QString typeChecks; - QTextStream tck(&typeChecks); - - QString pyArgName = (usePyArgs && maxArgs > 1) ? QString("pyargs[%1]").arg(overloadData->argPos()) : "arg"; - - OverloadData* od = overloadData; - int startArg = od->argPos(); - int sequenceArgCount = 0; - while (od && !od->argType()->isVarargs()) { - if (usePyArgs) - pyArgName = QString("pyargs[%1]").arg(od->argPos()); - - writeTypeCheck(tck, od, pyArgName); - 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 (!od->argType()->isVarargs()) - tck << " && "; - } - } - - s << "if ("; - 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; - s << "numArgs " << (lastArgIsVarargs ? ">=" : "==") << " " << numArgs << " && "; - } else if (sequenceArgCount > 1) { - s << "numArgs >= " << (startArg + sequenceArgCount) << " && "; - } - - if (refFunc->isOperatorOverload()) - s << (refFunc->isReverseOperator() ? "" : "!") << "isReverse && "; - - s << typeChecks << ") {" << 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 << "{" << endl; - { - Indentation indent(INDENT); - if (overloadData.hasAllowThread()) - s << INDENT << "Shiboken::ThreadStateSaver " THREAD_STATE_SAVER_VAR ";" << 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->minimalSignature() << endl; - s << INDENT << '{' << endl; - { - Indentation indent(INDENT); - writeSingleFunctionCall(s, overloadData, func); - s << INDENT << "break;" << endl; - } - s << INDENT << '}' << endl; - } - } - } - s << INDENT << '}' << endl; - } - s << INDENT << '}' << endl; -} - -void CppGenerator::writeSingleFunctionCall(QTextStream& s, const OverloadData& overloadData, const AbstractMetaFunction* func) -{ - 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; - } - - const AbstractMetaClass* implementingClass = overloadData.referenceFunction()->implementingClass(); - bool usePyArgs = pythonFunctionWrapperUsesListOfArguments(overloadData); - - // Handle named arguments. - writeNamedArgumentResolution(s, func, usePyArgs); - - int removedArgs = 0; - for (int i = 0; i < func->arguments().count(); i++) { - if (func->argumentRemoved(i + 1)) { - removedArgs++; - continue; - } - - if (!func->conversionRule(TypeSystem::NativeCode, i + 1).isEmpty()) - continue; - - const AbstractMetaArgument* arg = func->arguments().at(i); - - QString typeReplaced = func->typeReplaced(arg->argumentIndex() + 1); - const AbstractMetaType* argType = 0; - if (typeReplaced.isEmpty()) - argType = arg->type(); - else - argType = buildAbstractMetaTypeFromString(typeReplaced); - - if (argType) { - QString argName = QString("cpp_arg%1").arg(i - removedArgs); - QString pyArgName = usePyArgs ? QString("pyargs[%1]").arg(i - removedArgs) : "arg"; - QString defaultValue = guessScopeForDefaultValue(func, arg); - - writeArgumentConversion(s, argType, argName, pyArgName, implementingClass, defaultValue); - - // Free a custom type created by buildAbstractMetaTypeFromString. - if (argType != arg->type()) - delete argType; - } - } - - 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; -} - -void CppGenerator::writeNamedArgumentResolution(QTextStream& s, const AbstractMetaFunction* func, bool usePyArgs) -{ - AbstractMetaArgumentList args = OverloadData::getArgumentsWithDefaultValues(func); - if (!args.isEmpty()) { - s << INDENT << "if (kwds) {" << endl; - { - Indentation indent(INDENT); - s << INDENT << "const char* errorArgName = 0;" << endl; - s << INDENT << "PyObject* "; - foreach (const AbstractMetaArgument* arg, args) { - int pyArgIndex = arg->argumentIndex() - OverloadData::numberOfRemovedArguments(func, arg->argumentIndex()); - QString pyArgName = usePyArgs ? QString("pyargs[%1]").arg(pyArgIndex) : "arg"; - s << "value = PyDict_GetItemString(kwds, \"" << arg->name() << "\");" << endl; - s << INDENT << "if (value) {" << endl; - { - Indentation indent(INDENT); - s << INDENT << "if (" << pyArgName << ")" << endl; - { - Indentation indent(INDENT); - s << INDENT << "errorArgName = \"" << arg->name() << "\";" << endl; - } - s << INDENT << "else" << endl; - { - Indentation indent(INDENT); - s << INDENT << pyArgName << " = value;" << endl; - } - } - s << INDENT << '}' << endl; - s << INDENT; - } - s << "if (errorArgName) {" << endl; - { - Indentation indent(INDENT); - s << INDENT << "PyErr_Format(PyExc_TypeError, \"" << fullPythonFunctionName(func); - s << "(): got multiple values for keyword argument '%s'\", errorArgName);" << endl; - s << INDENT << "return " << m_currentErrorCode << ';' << endl; - } - s << INDENT << '}' << endl; - - } - s << INDENT << '}' << endl; - } -} - -QString CppGenerator::argumentNameFromIndex(const AbstractMetaFunction* func, int argIndex, const AbstractMetaClass** wrappedClass) -{ - *wrappedClass = 0; - QString pyArgName; - if (argIndex == -1) { - pyArgName = QString("self"); - *wrappedClass = func->implementingClass(); - } else if (argIndex == 0) { - if (func->type()) { - pyArgName = PYTHON_RETURN_VAR; - *wrappedClass = classes().findClass(func->type()->typeEntry()->name()); - } else { - ReportHandler::warning("Invalid Argument index on function modification: " + func->name()); - } - } else { - int realIndex = argIndex - 1 - OverloadData::numberOfRemovedArguments(func, argIndex - 1); - *wrappedClass = classes().findClass(func->arguments().at(realIndex)->type()->typeEntry()->name()); - if (argIndex == 1 - && OverloadData::isSingleArgument(getFunctionGroups(func->implementingClass())[func->name()])) - pyArgName = QString("arg"); - else - pyArgName = QString("pyargs[%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 (SbkBaseWrapper_containsCppWrapper(self)) {\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; - - if (func->allowThread()) - s << INDENT << THREAD_STATE_SAVER_VAR ".save();" << endl; - - 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; - } - - CodeSnipList convRules = getConversionRule(TypeSystem::NativeCode, func); - if (convRules.size()) - writeCodeSnips(s, convRules, CodeSnip::Beginning, TypeSystem::TargetLangCode, func); - - // Code to restore the threadSaver has been written? - bool threadRestored = false; - - if (!func->isUserAdded()) { - bool badModifications = false; - QStringList userArgs; - - if (!func->isCopyConstructor()) { - int removedArgs = 0; - for (int i = 0; i < maxArgs + removedArgs; i++) { - const AbstractMetaArgument* arg = func->arguments().at(i); - 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 - bool hasConversionRule = !func->conversionRule(TypeSystem::NativeCode, arg->argumentIndex() + 1).isEmpty(); - if (hasConversionRule) { - userArgs << arg->name() + "_out"; - } else { - if (arg->defaultValueExpression().isEmpty()) - badModifications = true; - else - userArgs << guessScopeForDefaultValue(func, arg); - } - } else { - int idx = arg->argumentIndex() - removedArgs; - QString argName; - - bool hasConversionRule = !func->conversionRule(TypeSystem::NativeCode, arg->argumentIndex() + 1).isEmpty(); - if (hasConversionRule) { - argName = arg->name() + "_out"; - } else { - argName = QString("cpp_arg%1").arg(idx); - if (shouldDereferenceArgumentPointer(arg)) - argName.prepend('*'); - } - 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 (!arg->defaultValueExpression().isEmpty()) - otherArgs.prepend(guessScopeForDefaultValue(func, arg)); - else if (hasConversionRule) - otherArgs.prepend(arg->name() + "_out"); - else - badModifications = true; - } - if (otherArgsModified) - userArgs << otherArgs; - } - - bool isCtor = false; - QString methodCall; - QTextStream mc(&methodCall); - - if (badModifications) { - // When an argument is removed from a method signature and no other - // means of calling the method is provided (as with code injection) - // the generator must write a compiler error line stating the situation. - if (func->injectedCodeSnips(CodeSnip::Any, TypeSystem::TargetLangCode).isEmpty()) { - qFatal(qPrintable("No way to call \"" + func->ownerClass()->name() - + "::" + func->minimalSignature() - +"\" with the modifications described in the type system file")); - } - } else if (func->isOperatorOverload()) { - QString firstArg("(*" CPP_SELF_VAR ")"); - 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); - mc << firstArg << ' ' << op << ' ' << secondArg; - } else { - mc << op << ' ' << secondArg; - } - } else if (!injectedCodeCallsCppFunction(func)) { - if (func->isConstructor() || func->isCopyConstructor()) { - isCtor = true; - QString className = wrapperName(func->ownerClass()); - mc << "new " << className << '('; - if (func->isCopyConstructor() && maxArgs == 1) { - mc << '*'; - QString arg("cpp_arg0"); - if (shouldGenerateCppWrapper(func->ownerClass())) - arg = QString("reinterpret_cast<%1*>(%2)").arg(className).arg(arg); - mc << arg; - } else { - mc << userArgs.join(", "); - } - mc << ')'; - } else { - if (func->ownerClass()) { -#ifndef AVOID_PROTECTED_HACK - if (!func->isStatic()) - mc << CPP_SELF_VAR "->"; - if (!func->isAbstract()) - mc << func->ownerClass()->qualifiedCppName() << "::"; - mc << func->originalName(); -#else - if (!func->isStatic()) { - if (func->isProtected()) - mc << "((" << wrapperName(func->ownerClass()) << "*) "; - mc << CPP_SELF_VAR << (func->isProtected() ? ")" : "") << "->"; - } - if (!func->isAbstract()) - mc << (func->isProtected() ? wrapperName(func->ownerClass()) : func->ownerClass()->qualifiedCppName()) << "::"; - mc << func->originalName() << (func->isProtected() ? "_protected" : ""); -#endif - } else { - mc << func->originalName(); - } - mc << '(' << userArgs.join(", ") << ')'; - } - } - - if (!injectedCodeCallsCppFunction(func)) { - s << INDENT; - if (isCtor) { - s << "cptr = "; - } else if (func->type() && !func->isInplaceOperator()) { -#ifdef AVOID_PROTECTED_HACK - QString enumName; - const AbstractMetaEnum* metaEnum = findAbstractMetaEnum(func->type()); - if (metaEnum) { - if (metaEnum->isProtected()) - enumName = protectedEnumSurrogateName(metaEnum); - else - enumName = func->type()->cppSignature(); - methodCall.prepend(enumName + '('); - methodCall.append(')'); - s << enumName; - } else -#endif - s << func->type()->cppSignature(); - s << " " CPP_RETURN_VAR " = "; - } - s << methodCall << ';' << endl; - - if (func->allowThread()) { - s << INDENT << THREAD_STATE_SAVER_VAR ".restore();" << endl; - threadRestored = true; - } - - if (!isCtor && !func->isInplaceOperator() && func->type()) { - s << INDENT << PYTHON_RETURN_VAR " = "; - writeToPythonConversion(s, func->type(), func->ownerClass(), CPP_RETURN_VAR); - s << ';' << endl; - } - } - } - - if (!threadRestored && func->allowThread()) - s << INDENT << THREAD_STATE_SAVER_VAR ".restore();" << 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 (!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) - 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; - if (arg_mod.ownerships[TypeSystem::TargetLangCode] == TypeSystem::TargetLangOwnership) { - s << "SbkBaseWrapper_setOwnership(" << pyArgName << ", true);"; - } else if (wrappedClass->hasVirtualDestructor()) { - if (arg_mod.index == 0) { - s << "SbkBaseWrapper_setOwnership(" PYTHON_RETURN_VAR ", 0);"; - } else { - s << "BindingManager::instance().transferOwnershipToCpp(" << pyArgName << ");"; - } - } else { - s << "BindingManager::instance().invalidateWrapper(" << pyArgName << ");"; - } - s << endl; - } - - } else if (!refcount_mods.isEmpty()) { - foreach (ArgumentModification arg_mod, refcount_mods) { - if (arg_mod.referenceCounts.first().action != ReferenceCount::Add) - continue; - const AbstractMetaClass* wrappedClass = 0; - QString pyArgName = argumentNameFromIndex(func, arg_mod.index, &wrappedClass); - if (pyArgName.isEmpty()) { - s << "#error Invalid reference count modification for argument " << arg_mod.index << endl << endl; - break; - } - - s << INDENT << "Shiboken::keepReference(reinterpret_cast(self), \""; - QString varName = arg_mod.referenceCounts.first().varName; - if (varName.isEmpty()) - varName = func->minimalSignature() + QString().number(arg_mod.index); - - s << varName << "\", " << pyArgName << ");" << 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, SbkBaseWrapperType* 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::writeExtendedIsConvertibleFunction(QTextStream& s, const TypeEntry* externalType, const QList& conversions) -{ - s << "static bool " << extendedIsConvertibleFunctionName(externalType) << "(PyObject* pyobj)" << endl; - s << '{' << endl; - s << INDENT << "return "; - bool isFirst = true; - foreach (const AbstractMetaClass* metaClass, conversions) { - Indentation indent(INDENT); - if (isFirst) - isFirst = false; - else - s << endl << INDENT << " || "; - s << cpythonIsConvertibleFunction(metaClass->typeEntry()) << "(pyobj)"; - } - s << ';' << endl; - s << '}' << endl; -} - -void CppGenerator::writeExtendedToCppFunction(QTextStream& s, const TypeEntry* externalType, const QList& conversions) -{ - s << "static void* " << extendedToCppFunctionName(externalType) << "(PyObject* pyobj)" << endl; - s << '{' << endl; - s << INDENT << "void* cptr = 0;" << endl; - bool isFirst = true; - foreach (const AbstractMetaClass* metaClass, conversions) { - s << INDENT; - if (isFirst) - isFirst = false; - else - s << "else "; - s << "if (" << cpythonIsConvertibleFunction(metaClass->typeEntry()) << "(pyobj))" << endl; - Indentation indent(INDENT); - s << INDENT << "cptr = new " << externalType->name() << '('; - writeToCppConversion(s, metaClass, "pyobj"); - s << ");" << endl; - } - s << INDENT << "return cptr;" << endl; - s << '}' << endl; -} - -void CppGenerator::writeExtendedConverterInitialization(QTextStream& s, const TypeEntry* externalType, const QList& conversions) -{ - s << INDENT << "// Extended implicit conversions for " << externalType->targetLangPackage() << '.' << externalType->name() << endl; - s << INDENT << "shiboType = reinterpret_cast("; - s << cppApiVariableName(externalType->targetLangPackage()) << '['; - s << getTypeIndexVariableName(externalType) << "]);" << endl; - s << INDENT << "shiboType->ext_isconvertible = " << extendedIsConvertibleFunctionName(externalType) << ';' << endl; - s << INDENT << "shiboType->ext_tocpp = " << extendedToCppFunctionName(externalType) << ';' << endl; -} - -QString CppGenerator::multipleInheritanceInitializerFunctionName(const AbstractMetaClass* metaClass) -{ - if (!hasMultipleInheritanceInAncestry(metaClass)) - return QString(); - return QString("%1_mi_init").arg(cpythonBaseName(metaClass->typeEntry())); -} - -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 && (reinterpret_cast(baseType)->type() == ContainerTypeEntry::ListContainer)) - 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 cpp_dtor('0'); - QString tp_as_number('0'); - QString tp_as_sequence('0'); - QString tp_hash('0'); - QString mi_init('0'); - QString obj_copier('0'); - QString mi_specialcast('0'); - QString cppClassName = metaClass->qualifiedCppName(); - QString className = cpythonTypeName(metaClass).replace(QRegExp("_Type$"), ""); - QString baseClassName('0'); - AbstractMetaFunctionList ctors = metaClass->queryFunctions(AbstractMetaClass::Constructors); - bool onlyPrivCtor = !metaClass->hasNonPrivateConstructor(); - - if (metaClass->hasArithmeticOperatorOverload() - || metaClass->hasLogicalOperatorOverload() - || metaClass->hasBitwiseOperatorOverload()) { - tp_as_number = QString("&%1_as_number").arg(cpythonBaseName(metaClass)); - } - - // sequence protocol check - if (supportsSequenceProtocol(metaClass)) - tp_as_sequence = QString("&Py%1_as_sequence").arg(cppClassName); - - if (!metaClass->baseClass()) - baseClassName = "reinterpret_cast(&Shiboken::SbkBaseWrapper_Type)"; - - if (metaClass->isNamespace() || metaClass->hasPrivateDestructor()) { - tp_flags = "Py_TPFLAGS_DEFAULT|Py_TPFLAGS_CHECKTYPES"; - tp_dealloc = metaClass->hasPrivateDestructor() ? - "Shiboken::deallocWrapperWithPrivateDtor" : "0"; - tp_init = "0"; - } else { - if (onlyPrivCtor) - tp_flags = "Py_TPFLAGS_DEFAULT|Py_TPFLAGS_CHECKTYPES"; - else - tp_flags = "Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_CHECKTYPES"; - - QString deallocClassName; - if (shouldGenerateCppWrapper(metaClass)) - deallocClassName = wrapperName(metaClass); - else - deallocClassName = cppClassName; - tp_dealloc = "&Shiboken::deallocWrapper"; - - QString dtorClassName = metaClass->qualifiedCppName(); -#ifdef AVOID_PROTECTED_HACK - if (metaClass->hasProtectedDestructor()) - dtorClassName = wrapperName(metaClass); -#endif - cpp_dtor = "&Shiboken::callCppDestructor<" + dtorClassName + " >"; - - tp_init = onlyPrivCtor ? "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 = "Shiboken::SbkBaseWrapper_TpNew"; - - 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); - } - - // class or some ancestor has multiple inheritance - const AbstractMetaClass* miClass = getMultipleInheritingClass(metaClass); - if (miClass) { - if (metaClass == miClass) { - mi_init = multipleInheritanceInitializerFunctionName(miClass); - writeMultipleInheritanceInitializerFunction(s, metaClass); - } - mi_specialcast = '&'+cpythonSpecialCastFunctionName(metaClass); - writeSpecialCastFunction(s, metaClass); - s << endl; - } - - if (metaClass->typeEntry()->isValue() && shouldGenerateCppWrapper(metaClass)) - obj_copier = '&' + cpythonBaseName(metaClass) + "_ObjCopierFunc"; - - if (!metaClass->typeEntry()->hashFunction().isEmpty()) - tp_hash = '&' + cpythonBaseName(metaClass) + "_HashFunc"; - - s << "// Class Definition -----------------------------------------------" << endl; - s << "extern \"C\" {" << endl; - s << "static SbkBaseWrapperType " << className + "_Type" << " = { { {" << endl; - s << INDENT << "PyObject_HEAD_INIT(&Shiboken::SbkBaseWrapperType_Type)" << endl; - s << INDENT << "/*ob_size*/ 0," << endl; - s << INDENT << "/*tp_name*/ \"" << metaClass->fullName() << "\"," << endl; - s << INDENT << "/*tp_basicsize*/ sizeof(Shiboken::SbkBaseWrapper)," << 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*/ " << tp_as_number << ',' << endl; - s << INDENT << "/*tp_as_sequence*/ " << tp_as_sequence << ',' << endl; - s << INDENT << "/*tp_as_mapping*/ 0," << endl; - s << INDENT << "/*tp_hash*/ " << tp_hash << ',' << endl; - s << INDENT << "/*tp_call*/ 0," << 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*/ 0," << endl; - s << INDENT << "/*tp_clear*/ 0," << endl; - s << INDENT << "/*tp_richcompare*/ " << tp_richcompare << ',' << endl; - s << INDENT << "/*tp_weaklistoffset*/ 0," << endl; - s << INDENT << "/*tp_iter*/ 0," << endl; - s << INDENT << "/*tp_iternext*/ 0," << 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 << "/*mi_offsets*/ 0," << endl; - s << INDENT << "/*mi_init*/ " << mi_init << ',' << endl; - s << INDENT << "/*mi_specialcast*/ " << mi_specialcast << ',' << endl; - s << INDENT << "/*type_discovery*/ 0," << endl; - s << INDENT << "/*obj_copier*/ " << obj_copier << ',' << endl; - s << INDENT << "/*ext_isconvertible*/ 0," << endl; - s << INDENT << "/*ext_tocpp*/ 0," << endl; - s << INDENT << "/*cpp_dtor*/ " << cpp_dtor << ',' << endl; - s << INDENT << "/*is_multicpp*/ 0," << endl; - s << INDENT << "/*is_user_type*/ 0," << endl; - QString suffix; - if (metaClass->typeEntry()->isObject() || metaClass->typeEntry()->isQObject()) - suffix = "*"; - s << INDENT << "/*original_name*/ \"" << metaClass->qualifiedCppName() << suffix << "\"," << endl; - s << INDENT << "/*user_data*/ 0" << endl; - s << "};" << endl; - s << "} //extern" << 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; - writeInvalidCppObjectCheck(s); - - 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) -{ - QString className = metaClass->qualifiedCppName(); - QMap funcs; - - bool hasFunctions = false; - foreach(QString funcName, m_sequenceProtocol.keys()) { - const AbstractMetaFunction* func = metaClass->findFunction(funcName); - funcs[funcName] = func ? cpythonFunctionName(func).prepend("&") : "0"; - if (!hasFunctions && func) - hasFunctions = true; - } - - //use default implementation - if (!hasFunctions) { - QString baseName = cpythonBaseName(metaClass->typeEntry()); - funcs["__len__"] = baseName + "__len__"; - funcs["__getitem__"] = baseName + "__getitem__"; - funcs["__setitem__"] = baseName + "__setitem__"; - } - - s << "static PySequenceMethods Py" << className << "_as_sequence = {\n" - << INDENT << "/*sq_length*/ " << funcs["__len__"] << ",\n" - << INDENT << "/*sq_concat*/ " << funcs["__concat__"] << ",\n" - << INDENT << "/*sq_repeat*/ 0,\n" - << INDENT << "/*sq_item*/ " << funcs["__getitem__"] << ",\n" - << INDENT << "/*sq_slice*/ " << funcs["__getslice__"] << ",\n" - << INDENT << "/*sq_ass_item*/ " << funcs["__setitem__"] << ",\n" - << INDENT << "/*sq_ass_slice*/ " << funcs["__setslice__"] << ",\n" - << INDENT << "/*sq_contains*/ " << funcs["__contains__"] << ",\n" - << INDENT << "/*sq_inplace_concat*/ 0,\n" - << INDENT << "/*sq_inplace_repeat*/ 0\n" - << "};\n\n"; -} - -void CppGenerator::writeTypeAsNumberDefinition(QTextStream& s, const AbstractMetaClass* metaClass) -{ - QMap nb; - - nb["__add__"] = QString('0'); - nb["__sub__"] = QString('0'); - nb["__mul__"] = QString('0'); - nb["__div__"] = QString('0'); - nb["__mod__"] = QString('0'); - nb["__neg__"] = QString('0'); - nb["__pos__"] = QString('0'); - nb["__invert__"] = QString('0'); - nb["__lshift__"] = QString('0'); - nb["__rshift__"] = QString('0'); - nb["__and__"] = QString('0'); - nb["__xor__"] = QString('0'); - nb["__or__"] = QString('0'); - nb["__iadd__"] = QString('0'); - nb["__isub__"] = QString('0'); - nb["__imul__"] = QString('0'); - nb["__idiv__"] = QString('0'); - nb["__imod__"] = QString('0'); - nb["__ilshift__"] = QString('0'); - nb["__irshift__"] = QString('0'); - nb["__iand__"] = QString('0'); - nb["__ixor__"] = QString('0'); - nb["__ior__"] = QString('0'); - - 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); - } - - s << "static PyNumberMethods " << cpythonBaseName(metaClass); - s << "_as_number = {" << endl; - s << INDENT << "/*nb_add*/ (binaryfunc)" << nb["__add__"] << ',' << endl; - s << INDENT << "/*nb_subtract*/ (binaryfunc)" << nb["__sub__"] << ',' << endl; - s << INDENT << "/*nb_multiply*/ (binaryfunc)" << nb["__mul__"] << ',' << endl; - s << INDENT << "/*nb_divide*/ (binaryfunc)" << nb["__div__"] << ',' << endl; - s << INDENT << "/*nb_remainder*/ (binaryfunc)" << nb["__mod__"] << ',' << endl; - s << INDENT << "/*nb_divmod*/ 0," << endl; - s << INDENT << "/*nb_power*/ 0," << endl; - s << INDENT << "/*nb_negative*/ (unaryfunc)" << nb["__neg__"] << ',' << endl; - s << INDENT << "/*nb_positive*/ (unaryfunc)" << nb["__pos__"] << ',' << endl; - s << INDENT << "/*nb_absolute*/ 0," << endl; - s << INDENT << "/*nb_nonzero*/ 0," << endl; - s << INDENT << "/*nb_invert*/ (unaryfunc)" << nb["__invert__"] << ',' << endl; - s << INDENT << "/*nb_lshift*/ (binaryfunc)" << nb["__lshift__"] << ',' << endl; - s << INDENT << "/*nb_rshift*/ (binaryfunc)" << nb["__rshift__"] << ',' << endl; - s << INDENT << "/*nb_and*/ (binaryfunc)" << nb["__and__"] << ',' << endl; - s << INDENT << "/*nb_xor*/ (binaryfunc)" << nb["__xor__"] << ',' << endl; - s << INDENT << "/*nb_or*/ (binaryfunc)" << nb["__or__"] << ',' << endl; - s << INDENT << "/*nb_coerce*/ 0," << endl; - s << INDENT << "/*nb_int*/ 0," << endl; - s << INDENT << "/*nb_long*/ 0," << endl; - s << INDENT << "/*nb_float*/ 0," << endl; - s << INDENT << "/*nb_oct*/ 0," << endl; - s << INDENT << "/*nb_hex*/ 0," << endl; - s << INDENT << "/*nb_inplace_add*/ (binaryfunc)" << nb["__iadd__"] << ',' << endl; - s << INDENT << "/*nb_inplace_subtract*/ (binaryfunc)" << nb["__isub__"] << ',' << endl; - s << INDENT << "/*nb_inplace_multiply*/ (binaryfunc)" << nb["__imul__"] << ',' << endl; - s << INDENT << "/*nb_inplace_divide*/ (binaryfunc)" << nb["__idiv__"] << ',' << endl; - s << INDENT << "/*nb_inplace_remainder*/ (binaryfunc)" << nb["__imod__"] << ',' << endl; - s << INDENT << "/*nb_inplace_power*/ 0," << endl; - s << INDENT << "/*nb_inplace_lshift*/ (binaryfunc)" << nb["__ilshift__"] << ',' << endl; - s << INDENT << "/*nb_inplace_rshift*/ (binaryfunc)" << nb["__irshift__"] << ',' << endl; - s << INDENT << "/*nb_inplace_and*/ (binaryfunc)" << nb["__iand__"] << ',' << endl; - s << INDENT << "/*nb_inplace_xor*/ (binaryfunc)" << nb["__ixor__"] << ',' << endl; - s << INDENT << "/*nb_inplace_or*/ (binaryfunc)" << nb["__ior__"] << ',' << 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::writeGetterFunction(QTextStream& s, const AbstractMetaField* metaField) -{ - s << "static PyObject* " << cpythonGetterFunctionName(metaField) << "(PyObject* self, void*)" << endl; - s << '{' << endl; - s << INDENT << "return "; - - QString cppField; -#ifdef AVOID_PROTECTED_HACK - if (metaField->isProtected()) - cppField = QString("((%1*)%2)->%3()").arg(wrapperName(metaField->enclosingClass())).arg(cpythonWrapperCPtr(metaField->enclosingClass(), "self")).arg(protectedFieldGetterName(metaField)); - else -#endif - cppField= QString("%1->%2").arg(cpythonWrapperCPtr(metaField->enclosingClass(), "self")).arg(metaField->name()); - writeToPythonConversion(s, metaField->type(), metaField->enclosingClass(), cppField); - s << ';' << endl; - s << '}' << endl; -} - -void CppGenerator::writeSetterFunction(QTextStream& s, const AbstractMetaField* metaField) -{ - s << "static int " << cpythonSetterFunctionName(metaField) << "(PyObject* self, PyObject* value, void*)" << endl; - s << '{' << endl; - - s << INDENT << "if (value == 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; - - s << INDENT << "if (!"; - writeTypeCheck(s, metaField->type(), "value", isNumber(metaField->type()->typeEntry())); - s << ") {" << endl; - { - Indentation indent(INDENT); - s << INDENT << "PyErr_SetString(PyExc_TypeError, \"wrong type attributed to '"; - s << metaField->name() << "', '" << metaField->type()->name() << "' or convertible type expected\");" << endl; - s << INDENT << "return -1;" << endl; - } - s << INDENT << '}' << endl << endl; - - s << INDENT; -#ifdef AVOID_PROTECTED_HACK - if (metaField->isProtected()) { - QString fieldStr = QString("((%1*)%2)->%3").arg(wrapperName(metaField->enclosingClass())).arg(cpythonWrapperCPtr(metaField->enclosingClass(), "self")).arg(protectedFieldSetterName(metaField)); - s << fieldStr << '('; - writeToCppConversion(s, metaField->type(), metaField->enclosingClass(), "value"); - s << ')'; - } else { -#endif - QString fieldStr = QString("%1->%2").arg(cpythonWrapperCPtr(metaField->enclosingClass(), "self")).arg(metaField->name()); - s << fieldStr << " = "; - writeToCppConversion(s, metaField->type(), metaField->enclosingClass(), "value"); -#ifdef AVOID_PROTECTED_HACK - } -#endif - s << ';' << endl << endl; - - - bool pythonWrapperRefCounting = metaField->type()->typeEntry()->isObject() - || metaField->type()->isValuePointer(); - if (pythonWrapperRefCounting) { - s << INDENT << "Shiboken::keepReference(reinterpret_cast(self), \""; - s << metaField->name() << "\", value);" << endl; - //s << INDENT << "Py_XDECREF(oldvalue);" << endl; - s << 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* self, PyObject* other, int op)" << endl; - s << '{' << endl; - QList cmpOverloads = filterGroupedOperatorFunctions(metaClass, AbstractMetaClass::ComparisonOp); - s << INDENT << "bool result = false;" << endl; - s << INDENT << metaClass->qualifiedCppName() << "& cpp_self = *" << cpythonWrapperCPtr(metaClass) << ';' << endl; - s << endl; - - s << INDENT << "switch (op) {" << endl; - { - Indentation indent(INDENT); - foreach (AbstractMetaFunctionList overloads, cmpOverloads) { - OverloadData overloadData(overloads, this); - const AbstractMetaFunction* rfunc = overloads[0]; - - s << INDENT << "case " << ShibokenGenerator::pythonRichCompareOperatorId(rfunc) << ':' << 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; - bool comparesWithSameType = false; - foreach (const AbstractMetaFunction* func, overloads) { - if (func->isStatic()) - continue; - - const AbstractMetaType* type = func->arguments()[0]->type(); - bool numberType = alternativeNumericTypes == 1 || ShibokenGenerator::isPyInt(type); - - if (!comparesWithSameType) - comparesWithSameType = type->typeEntry() == metaClass->typeEntry(); - - if (!first) { - s << " else "; - } else { - first = false; - s << INDENT; - } - - s << "if (" << cpythonIsConvertibleFunction(type, numberType) << "(other)) {" << endl; - { - Indentation indent(INDENT); - s << INDENT << "// " << func->signature() << endl; - s << INDENT; - AbstractMetaClass* clz = classes().findClass(type->typeEntry()); - if (type->typeEntry()->isValue()) { - Q_ASSERT(clz); - s << clz->qualifiedCppName() << '*'; - } else - s << translateTypeForWrapperMethod(type, metaClass); - s << " cpp_other = "; - if (type->typeEntry()->isValue()) - s << cpythonWrapperCPtr(type, "other"); - else - writeToCppConversion(s, type, metaClass, "other"); - s << ';' << endl; - - s << INDENT << "result = "; - // It's a value type and the conversion for a pointer returned null. - if (type->typeEntry()->isValue()) { - s << "!cpp_other ? cpp_self == "; - writeToCppConversion(s, type, metaClass, "other", ExcludeReference | ExcludeConst); - s << " : "; - } - s << "(cpp_self " << op << ' ' << (type->typeEntry()->isValue() ? "(*" : ""); - s << "cpp_other" << (type->typeEntry()->isValue() ? ")" : "") << ");" << endl; - } - s << INDENT << '}'; - } - - // Compares with implicit conversions - if (comparesWithSameType && !metaClass->implicitConversions().isEmpty()) { - AbstractMetaType temporaryType; - temporaryType.setTypeEntry(metaClass->typeEntry()); - temporaryType.setConstant(true); - temporaryType.setReference(false); - temporaryType.setTypeUsagePattern(AbstractMetaType::ValuePattern); - s << " else if (" << cpythonIsConvertibleFunction(metaClass->typeEntry()); - s << "(other)) {" << endl; - { - Indentation indent(INDENT); - writeArgumentConversion(s, &temporaryType, "cpp_other", "other", metaClass); - s << INDENT << "result = (cpp_self " << op << " (*cpp_other));" << endl; - } - s << INDENT << '}'; - } - - s << " else goto " << baseName << "_RichComparison_TypeError;" << endl; - s << endl; - - s << INDENT << "break;" << endl; - } - s << INDENT << "default:" << endl; - { - Indentation indent(INDENT); - s << INDENT << "PyErr_SetString(PyExc_NotImplementedError, \"operator not implemented.\");" << endl; - s << INDENT << "return " << m_currentErrorCode << ';' << endl; - } - } - s << INDENT << '}' << endl << endl; - - s << INDENT << "if (result)" << endl; - { - Indentation indent(INDENT); - s << INDENT << "Py_RETURN_TRUE;" << endl; - } - s << INDENT << baseName << "_RichComparison_TypeError:" << endl; - s << INDENT << "Py_RETURN_FALSE;" << 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::writeEnumInitialization(QTextStream& s, const AbstractMetaEnum* cppEnum) -{ - QString cpythonName = cpythonEnumName(cppEnum); - QString addFunction; - if (cppEnum->enclosingClass()) - addFunction = "PyDict_SetItemString(" + cpythonTypeName(cppEnum->enclosingClass()) + ".super.ht_type.tp_dict,"; - else if (cppEnum->isAnonymous()) - addFunction = "PyModule_AddIntConstant(module,"; - else - addFunction = "PyModule_AddObject(module,"; - - s << INDENT << "// init "; - if (cppEnum->isAnonymous()) - s << "anonymous enum identified by enum value: "; - else - s << "enum: "; - s << cppEnum->name() << endl; - - if (!cppEnum->isAnonymous()) { - s << INDENT << cpythonTypeNameExt(cppEnum->typeEntry()) << " = &" << cpythonTypeName(cppEnum->typeEntry()) << ';' << endl; - s << INDENT << "if (PyType_Ready((PyTypeObject*)&" << cpythonName << "_Type) < 0)" << endl; - s << INDENT << INDENT << "return;" << endl; - - s << INDENT << "Py_INCREF(&" << cpythonName << "_Type);" << endl; - - s << INDENT << addFunction << endl; - s << INDENT << INDENT << INDENT << '\"' << cppEnum->name() << "\","; - s << "((PyObject*)&" << cpythonName << "_Type));" << endl << endl; - - FlagsTypeEntry* flags = cppEnum->typeEntry()->flags(); - if (flags) { - QString flagsName = cpythonFlagsName(flags); - s << INDENT << "// init flags class: " << flags->name() << endl; - s << INDENT << cpythonTypeNameExt(flags) << " = &" << cpythonTypeName(flags) << ';' << endl; - - s << INDENT << "if (PyType_Ready((PyTypeObject*)&" << flagsName << "_Type) < 0)" << endl; - s << INDENT << INDENT << "return;" << endl; - - s << INDENT << "Py_INCREF(&" << flagsName << "_Type);" << endl; - - s << INDENT << addFunction << endl; - s << INDENT << INDENT << INDENT << '\"' << flags->flagsName() << "\","; - s << "((PyObject*)&" << flagsName << "_Type));" << endl << endl; - } - } - - - foreach (const AbstractMetaEnumValue* enumValue, cppEnum->values()) { - if (cppEnum->typeEntry()->isEnumValueRejected(enumValue->name())) - continue; - - QString enumValueText; -#ifdef AVOID_PROTECTED_HACK - if (!cppEnum->isProtected()) { -#endif - enumValueText = "(long) "; - if (cppEnum->enclosingClass()) - enumValueText += cppEnum->enclosingClass()->qualifiedCppName() + "::"; - enumValueText += enumValue->name(); -#ifdef AVOID_PROTECTED_HACK - } else { - enumValueText += enumValue->value(); - } -#endif - - bool shouldDecrefNumber = false; - QString enumItemText = "enum_item"; - if (!cppEnum->isAnonymous()) { - s << INDENT << "enum_item = Shiboken::SbkEnumObject_New(&"; - s << cpythonName << "_Type," << endl; - { - Indentation indent(INDENT); - s << INDENT << enumValueText << ", \"" << enumValue->name() << "\");" << endl; - } - } else if (cppEnum->enclosingClass()) { - s << INDENT << "enum_item = PyInt_FromLong(" << enumValueText << ");" << endl; - shouldDecrefNumber = true; - } else { - enumItemText = enumValueText; - } - - s << INDENT << addFunction << endl; - { - Indentation indent(INDENT); - s << INDENT << '"' << enumValue->name() << "\", " << enumItemText << ");" << endl; - } - if (shouldDecrefNumber) - s << INDENT << "Py_DECREF(enum_item);" << endl; - - if (!cppEnum->isAnonymous()) { - s << INDENT << "PyDict_SetItemString(" << cpythonName << "_Type.tp_dict," << endl; - { - Indentation indent(INDENT); - s << INDENT << '"' << enumValue->name() << "\", enum_item);" << endl; - } - } - - } - - if (!cppEnum->isAnonymous()) { - // TypeResolver stuff - s << INDENT << "Shiboken::TypeResolver::createValueTypeResolver(\""; - if (cppEnum->enclosingClass()) - s << cppEnum->enclosingClass()->qualifiedCppName() << "::"; - s << cppEnum->name() << "\");\n"; - } - - - s << endl; -} - -void CppGenerator::writeSignalInitialization(QTextStream& s, const AbstractMetaClass* metaClass) -{ - QHash signatures; - - foreach (const AbstractMetaFunction* cppSignal, metaClass->cppSignalFunctions()) { - QString signature; - if (cppSignal->declaringClass() == metaClass) { - if (cppSignal->arguments().count()) { - for (int i = 0; i < cppSignal->arguments().count(); ++i) { - if (i > 0) - signature += ", "; - AbstractMetaArgument *a = cppSignal->arguments().at(i); - signature += a->type()->cppSignature(); - } - } else { - signature = "void"; - } - signatures[cppSignal->name()].append(QMetaObject::normalizedSignature(signature.toAscii())); - } - } - - if (signatures.size() == 0) - return; - - s << INDENT << "// Initialize signals" << endl; - s << INDENT << "PyObject* signal_item;" << endl << endl; - - foreach(QString funcName, signatures.keys()) { - s << INDENT << "signal_item = PySide::signalNew(\"" << funcName <<"\""; - foreach(QString signature, signatures[funcName]) - s << ", \"" + signature << "\""; - s << ", NULL);" << endl; - s << INDENT << "PyDict_SetItemString(" + cpythonTypeName(metaClass) + ".super.ht_type.tp_dict"; - s << ", \"" << funcName << "\", signal_item);" << endl; - s << INDENT << "Py_DECREF(signal_item);" << endl; - } - s << endl; -} - -void CppGenerator::writeEnumNewMethod(QTextStream& s, const AbstractMetaEnum* cppEnum) -{ - QString cpythonName = cpythonEnumName(cppEnum); - s << "static PyObject* "; - s << cpythonName << "_New(PyTypeObject* type, PyObject* args, PyObject* kwds)" << endl; - s << '{' << endl; - s << INDENT << "int item_value = 0;" << endl; - s << INDENT << "if (!PyArg_ParseTuple(args, \"|i:__new__\", &item_value))" << endl; - { - Indentation indent(INDENT); - s << INDENT << "return 0;" << endl; - } - s << INDENT << "PyObject* self = Shiboken::SbkEnumObject_New(type, item_value);" << endl << endl; - s << INDENT << "if (!self)" << endl; - { - Indentation indent(INDENT); - s << INDENT << "return 0;" << endl; - } - s << INDENT << "return self;" << endl << '}' << endl; -} - -void CppGenerator::writeEnumDefinition(QTextStream& s, const AbstractMetaEnum* cppEnum) -{ - QString cpythonName = cpythonEnumName(cppEnum); - QString tp_as_number("0"); - if (cppEnum->typeEntry()->flags()) - tp_as_number = QString("&%1_as_number").arg(cpythonName); - - - s << "static PyGetSetDef " << cpythonName << "_getsetlist[] = {" << endl; - s << INDENT << "{const_cast(\"name\"), (getter)Shiboken::SbkEnumObject_name}," << endl; - s << INDENT << "{0} // Sentinel" << endl; - s << "};" << endl << endl; - - QString newFunc = cpythonName + "_New"; - - s << "// forward declaration of new function" << endl; - s << "static PyObject* " << newFunc << "(PyTypeObject*, PyObject*, PyObject*);" << endl << endl; - - s << "static PyTypeObject " << cpythonName << "_Type = {" << endl; - s << INDENT << "PyObject_HEAD_INIT(&Shiboken::SbkEnumType_Type)" << endl; - s << INDENT << "/*ob_size*/ 0," << endl; - s << INDENT << "/*tp_name*/ \"" << cppEnum->name() << "\"," << endl; - s << INDENT << "/*tp_basicsize*/ sizeof(Shiboken::SbkEnumObject)," << endl; - s << INDENT << "/*tp_itemsize*/ 0," << endl; - s << INDENT << "/*tp_dealloc*/ 0," << 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*/ Shiboken::SbkEnumObject_repr," << endl; - s << INDENT << "/*tp_as_number*/ " << tp_as_number << ',' << endl; - s << INDENT << "/*tp_as_sequence*/ 0," << endl; - s << INDENT << "/*tp_as_mapping*/ 0," << endl; - s << INDENT << "/*tp_hash*/ 0," << endl; - s << INDENT << "/*tp_call*/ 0," << endl; - s << INDENT << "/*tp_str*/ Shiboken::SbkEnumObject_repr," << endl; - s << INDENT << "/*tp_getattro*/ 0," << endl; - s << INDENT << "/*tp_setattro*/ 0," << endl; - s << INDENT << "/*tp_as_buffer*/ 0," << endl; - s << INDENT << "/*tp_flags*/ Py_TPFLAGS_DEFAULT," << endl; - s << INDENT << "/*tp_doc*/ 0," << endl; - s << INDENT << "/*tp_traverse*/ 0," << endl; - s << INDENT << "/*tp_clear*/ 0," << endl; - s << INDENT << "/*tp_richcompare*/ 0," << endl; - s << INDENT << "/*tp_weaklistoffset*/ 0," << endl; - s << INDENT << "/*tp_iter*/ 0," << endl; - s << INDENT << "/*tp_iternext*/ 0," << endl; - s << INDENT << "/*tp_methods*/ 0," << endl; - s << INDENT << "/*tp_members*/ 0," << endl; - s << INDENT << "/*tp_getset*/ " << cpythonName << "_getsetlist," << endl; - s << INDENT << "/*tp_base*/ &PyInt_Type," << 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*/ 0," << endl; - s << INDENT << "/*tp_alloc*/ 0," << endl; - s << INDENT << "/*tp_new*/ " << newFunc << ',' << 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 << endl; - - writeEnumNewMethod(s, cppEnum); - 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", "~"); - 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 << "/*nb_divide*/ 0," << 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*/ 0," << 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 << "/*nb_coerce*/ 0," << endl; - s << INDENT << "/*nb_int*/ 0," << endl; - s << INDENT << "/*nb_long*/ 0," << endl; - s << INDENT << "/*nb_float*/ 0," << endl; - s << INDENT << "/*nb_oct*/ 0," << endl; - s << INDENT << "/*nb_hex*/ 0," << endl; - s << INDENT << "/*nb_inplace_add*/ 0," << endl; - s << INDENT << "/*nb_inplace_subtract*/ 0," << endl; - s << INDENT << "/*nb_inplace_multiply*/ 0," << endl; - s << INDENT << "/*nb_inplace_divide*/ 0," << 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::writeFlagsDefinition(QTextStream& s, const AbstractMetaEnum* cppEnum) -{ - FlagsTypeEntry* flagsEntry = cppEnum->typeEntry()->flags(); - if (!flagsEntry) - return; - QString cpythonName = cpythonFlagsName(flagsEntry); - QString enumName = cpythonEnumName(cppEnum); - - s << "// forward declaration of new function" << endl; - s << "static PyTypeObject " << cpythonName << "_Type = {" << endl; - s << INDENT << "PyObject_HEAD_INIT(&PyType_Type)" << endl; - s << INDENT << "/*ob_size*/ 0," << endl; - s << INDENT << "/*tp_name*/ \"" << flagsEntry->flagsName() << "\"," << endl; - s << INDENT << "/*tp_basicsize*/ 0," << endl; - s << INDENT << "/*tp_itemsize*/ 0," << endl; - s << INDENT << "/*tp_dealloc*/ 0," << 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*/ 0," << endl; - s << INDENT << "/*tp_as_number*/ " << enumName << "_Type.tp_as_number," << endl; - s << INDENT << "/*tp_as_sequence*/ 0," << endl; - s << INDENT << "/*tp_as_mapping*/ 0," << endl; - s << INDENT << "/*tp_hash*/ 0," << endl; - s << INDENT << "/*tp_call*/ 0," << endl; - s << INDENT << "/*tp_str*/ 0," << endl; - s << INDENT << "/*tp_getattro*/ 0," << endl; - s << INDENT << "/*tp_setattro*/ 0," << endl; - s << INDENT << "/*tp_as_buffer*/ 0," << endl; - s << INDENT << "/*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES," << endl; - s << INDENT << "/*tp_doc*/ 0," << endl; - s << INDENT << "/*tp_traverse*/ 0," << endl; - s << INDENT << "/*tp_clear*/ 0," << endl; - s << INDENT << "/*tp_richcompare*/ 0," << endl; - s << INDENT << "/*tp_weaklistoffset*/ 0," << endl; - s << INDENT << "/*tp_iter*/ 0," << endl; - s << INDENT << "/*tp_iternext*/ 0," << endl; - s << INDENT << "/*tp_methods*/ 0," << endl; - s << INDENT << "/*tp_members*/ 0," << endl; - s << INDENT << "/*tp_getset*/ 0," << endl; - s << INDENT << "/*tp_base*/ &PyInt_Type," << 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*/ 0," << endl; - s << INDENT << "/*tp_alloc*/ 0," << endl; - s << INDENT << "/*tp_new*/ PyInt_Type.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 << endl; -} - -void CppGenerator::writeFlagsBinaryOperator(QTextStream& s, const AbstractMetaEnum* cppEnum, - QString pyOpName, QString cppOpName) -{ - FlagsTypeEntry* flagsEntry = cppEnum->typeEntry()->flags(); - Q_ASSERT(flagsEntry); - - QString converter = "Shiboken::Converter<" + flagsEntry->originalName() + " >::"; - - s << "PyObject* " << cpythonEnumName(cppEnum) << "___" << pyOpName << "__(PyObject* self, PyObject* arg)" << endl; - s << '{' << endl; - - // We need to check the type of self because self and arg can be swapped - s << INDENT << "if (" << converter << "checkType(self) && " << converter << "checkType(arg))" << endl; - s << INDENT << '{' << endl; - { - Indentation indent(INDENT); - s << INDENT << "PyErr_Format(PyExc_TypeError, \"unsupported operand type(s) for %s: '%s' and '%s'\", \"" - << cppOpName << "\", self->ob_type->tp_name, arg->ob_type->tp_name);" << endl; - s << INDENT << "return 0;" << endl; - } - s << INDENT << '}' << endl << endl; - - s << INDENT << "return Shiboken::Converter< " << flagsEntry->originalName() << " >::toPython(" << endl; - { - Indentation indent(INDENT); - s << INDENT << "Shiboken::Converter<" << flagsEntry->originalName() << ">::toCpp(self)" << endl; - s << INDENT << cppOpName << " Shiboken::Converter< "; - s << flagsEntry->originalName() << " >::toCpp(arg)" << endl; - } - s << INDENT << ");" << 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); - - QString converter = "Shiboken::Converter<" + flagsEntry->originalName() + " >::"; - - s << "PyObject* " << cpythonEnumName(cppEnum) << "___" << pyOpName << "__(PyObject* self, PyObject* arg)" << endl; - s << '{' << endl; - s << INDENT << "return Shiboken::Converter< " << (boolResult ? "bool" : flagsEntry->originalName()); - s << " >::toPython(" << endl; - { - Indentation indent(INDENT); - s << INDENT << cppOpName << converter << "toCpp(self)" << endl; - } - s << INDENT << ");" << endl; - s << '}' << endl << endl; -} - -void CppGenerator::writeClassRegister(QTextStream& s, const AbstractMetaClass* metaClass) -{ - QString pyTypeName = cpythonTypeName(metaClass); - s << "void init_" << metaClass->qualifiedCppName().replace("::", "_") << "(PyObject* module)" << endl; - s << '{' << endl; - s << INDENT << cpythonTypeNameExt(metaClass->typeEntry()) << " = reinterpret_cast(&" << cpythonTypeName(metaClass->typeEntry()) << ");" << endl << endl; - - // class inject-code target/beginning - if (!metaClass->typeEntry()->codeSnips().isEmpty()) { - writeCodeSnips(s, metaClass->typeEntry()->codeSnips(), CodeSnip::Beginning, TypeSystem::TargetLangCode, 0, 0, metaClass); - s << endl; - } - - if (metaClass->baseClass()) - s << INDENT << pyTypeName << ".super.ht_type.tp_base = " << cpythonTypeNameExt(metaClass->baseClass()->typeEntry()) << ';' << endl; - // Multiple inheritance - const AbstractMetaClassList baseClasses = getBaseClasses(metaClass); - if (metaClass->baseClassNames().size() > 1) { - s << INDENT << pyTypeName << ".super.ht_type.tp_bases = PyTuple_Pack("; - s << baseClasses.size(); - s << ',' << endl; - QStringList bases; - foreach (const AbstractMetaClass* base, baseClasses) - bases << "(PyTypeObject*)"+cpythonTypeNameExt(base->typeEntry()); - Indentation indent(INDENT); - s << INDENT << bases.join(", ") << ");" << endl << endl; - } - - // Fill multiple inheritance init function, if needed. - const AbstractMetaClass* miClass = getMultipleInheritingClass(metaClass); - if (miClass && miClass != metaClass) { - s << INDENT << cpythonTypeName(metaClass) << ".mi_init = "; - s << "reinterpret_cast(" + cpythonTypeNameExt(miClass->typeEntry()) + ")->mi_init;" << endl << endl; - } - - // Set typediscovery struct or fill the struct of another one - if (metaClass->isPolymorphic()) { - s << INDENT << "// Fill type discovery information" << endl; - if (metaClass->baseClass()) { - s << INDENT << cpythonTypeName(metaClass) << ".type_discovery = &" << cpythonBaseName(metaClass) << "_typeDiscovery;" << endl; - s << INDENT << "Shiboken::BindingManager& bm = Shiboken::BindingManager::instance();" << endl; - foreach (const AbstractMetaClass* base, baseClasses) { - s << INDENT << "bm.addClassInheritance(reinterpret_cast(" << cpythonTypeNameExt(base->typeEntry()) << "), &" << cpythonTypeName(metaClass) << ");" << endl; - } - } - s << endl; - } - - s << INDENT << "if (PyType_Ready((PyTypeObject*)&" << pyTypeName << ") < 0)" << endl; - s << INDENT << INDENT << "return;" << endl << endl; - - if (metaClass->enclosingClass() && (metaClass->enclosingClass()->typeEntry()->codeGeneration() != TypeEntry::GenerateForSubclass) ) { - s << INDENT << "PyDict_SetItemString(module," - << "\"" << metaClass->name() << "\", (PyObject*)&" << pyTypeName << ");" << endl; - } else { - s << INDENT << "Py_INCREF(reinterpret_cast(&" << pyTypeName << "));" << endl; - s << INDENT << "PyModule_AddObject(module, \"" << metaClass->name() << "\"," << endl; - Indentation indent(INDENT); - s << INDENT << "((PyObject*)&" << pyTypeName << "));" << endl << endl; - } - - if (!metaClass->enums().isEmpty()) { - s << INDENT << "// Initialize enums" << endl; - s << INDENT << "PyObject* enum_item;" << endl << endl; - } - - foreach (const AbstractMetaEnum* cppEnum, metaClass->enums()) - writeEnumInitialization(s, cppEnum); - - 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 (!metaClass->typeEntry()->codeSnips().isEmpty()) { - s << endl; - writeCodeSnips(s, metaClass->typeEntry()->codeSnips(), CodeSnip::End, TypeSystem::TargetLangCode, 0, 0, metaClass); - } - - if (!metaClass->isNamespace()) { - bool isObjectType = metaClass->typeEntry()->isObject(); - QString typeName = metaClass->qualifiedCppName(); - if (!isObjectType) - s << INDENT << "Shiboken::TypeResolver::createValueTypeResolver<" << typeName << " >" << "(\"" << typeName << "\");\n"; - - s << INDENT << "Shiboken::TypeResolver::createObjectTypeResolver<" << typeName << " >" << "(\"" << typeName << "*\");\n"; - - QString functionSufix = (isObjectType ? "Object" : "Value"); - s << INDENT << "Shiboken::TypeResolver::create" << functionSufix; - s << "TypeResolver<" << typeName << " >" << "(typeid(" << typeName << ").name());\n"; - if (shouldGenerateCppWrapper(metaClass)) { - s << INDENT << "Shiboken::TypeResolver::create" << functionSufix; - s << "TypeResolver<" << typeName << " >" << "(typeid(" << wrapperName(metaClass) << ").name());\n"; - } - } - - if (usePySideExtensions() && !metaClass->isNamespace()) { - // Qt metatypes are registered only on their first use, so we do this now. - const char* star = metaClass->typeEntry()->isObject() ? "*" : ""; - s << INDENT << "PySide::initQtMetaType<" << metaClass->qualifiedCppName() << star << " >();" << endl; - } - - s << '}' << endl << endl; -} - -void CppGenerator::writeTypeDiscoveryFunction(QTextStream& s, const AbstractMetaClass* metaClass) -{ - QString polymorphicExpr = metaClass->typeEntry()->polymorphicIdValue(); - - s << "static SbkBaseWrapperType* " << cpythonBaseName(metaClass) << "_typeDiscovery(void* cptr, SbkBaseWrapperType* instanceType)\n{" << endl; - - if (!metaClass->baseClass()) { - s << INDENT << "TypeResolver* typeResolver = TypeResolver::get(typeid(*reinterpret_cast<" - << metaClass->qualifiedCppName() << "*>(cptr)).name());" << endl; - s << INDENT << "if (typeResolver)" << endl; - { - Indentation indent(INDENT); - s << INDENT << "return reinterpret_cast(typeResolver->pythonType());" << endl; - } - } else if (!polymorphicExpr.isEmpty()) { - polymorphicExpr = polymorphicExpr.replace("%1", " reinterpret_cast<"+metaClass->qualifiedCppName()+"*>(cptr)"); - s << INDENT << " if (" << polymorphicExpr << ")" << endl; - { - Indentation indent(INDENT); - s << INDENT << "return &" << cpythonTypeName(metaClass) << ';' << 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() << " >()) && dynamic_cast<" << metaClass->qualifiedCppName() - << "*>(reinterpret_cast<"<< ancestor->qualifiedCppName() << "*>(cptr)))" << endl; - Indentation indent(INDENT); - s << INDENT << "return &" << cpythonTypeName(metaClass) << ';' << 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-expresison 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* self, PyObject* name, PyObject* value)" << endl; - s << '{' << endl; - if (usePySideExtensions()) { - s << INDENT << "Shiboken::AutoDecRef pp(PySide::qproperty_get_object(self, name));" << endl; - s << INDENT << "if (!pp.isNull())" << endl; - Indentation indent(INDENT); - s << INDENT << INDENT << "return PySide::qproperty_set(pp, self, value);" << endl; - } - s << INDENT << "return PyObject_GenericSetAttr(self, name, value);" << endl; - s << '}' << endl; -} - -void CppGenerator::writeGetattroFunction(QTextStream& s, const AbstractMetaClass* metaClass) -{ - s << "static PyObject* " << cpythonGetattroFunctionName(metaClass) << "(PyObject* self, PyObject* name)" << endl; - s << '{' << endl; - if (classNeedsGetattroFunction(metaClass)) { - s << INDENT << "if (self) {" << endl; - { - Indentation indent(INDENT); - s << INDENT << "if (SbkBaseWrapper_instanceDict(self)) {" << endl; - { - Indentation indent(INDENT); - s << INDENT << "PyObject* meth = PyDict_GetItem(SbkBaseWrapper_instanceDict(self), 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 << "const char* cname = PyString_AS_STRING(name);" << endl; - foreach (const AbstractMetaFunction* func, getMethodsWithBothStaticAndNonStaticMethods(metaClass)) { - s << INDENT << "if (strcmp(cname, \"" << func->name() << "\") == 0)" << endl; - Indentation indent(INDENT); - s << INDENT << "return PyCFunction_NewEx(&" << cpythonMethodDefinitionName(func) << ", self, 0);" << endl; - } - } - s << INDENT << '}' << endl; - } - s << INDENT << "PyObject* attr = PyObject_GenericGetAttr(self, name);" << endl; - if (usePySideExtensions() && metaClass->isQObject()) { - s << INDENT << "if (attr && PySide::isQPropertyType(attr)) {" << endl; - { - Indentation indent(INDENT); - s << INDENT << "PyObject *value = PySide::qproperty_get(attr, self);" << endl; - s << INDENT << "if (!value)" << endl; - { - Indentation indentation(INDENT); - s << INDENT << "return " << m_currentErrorCode << ';' << endl; - } - s << INDENT << "Py_DECREF(attr);" << endl; - s << INDENT << "Py_INCREF(value);" << endl; - s << INDENT << "attr = value;" << endl; - } - s << INDENT << "}" << endl; - } - s << INDENT << "return attr;" << 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); - } - - foreach (const AbstractMetaClass* cls, classes()) { - 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); - if (file.open(QFile::WriteOnly)) { - QTextStream s(&file); - - // write license comment - s << licenseComment() << endl; - - s << "#include " << endl; - s << "#include " << endl; - s << "#include " << endl; - - s << "#include \"" << getModuleHeaderFileName() << '"' << endl << endl; - foreach (const Include& include, includes) - s << include; - s << endl; - - TypeSystemTypeEntry* moduleEntry = reinterpret_cast(TypeDatabase::instance()->findType(packageName())); - CodeSnipList snips; - if (moduleEntry) - snips = moduleEntry->codeSnips(); - - // module inject-code native/beginning - if (!snips.isEmpty()) { - writeCodeSnips(s, snips, CodeSnip::Beginning, TypeSystem::NativeCode); - 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()) - continue; - writeEnumDefinition(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; - } - } - - s << "PyTypeObject** " << cppApiVariableName() << ";" << endl << endl;; - foreach (const QString& requiredModule, TypeDatabase::instance()->requiredTargetImports()) - s << "PyTypeObject** " << cppApiVariableName(requiredModule) << ";" << endl << endl;; - - s << "// Module initialization "; - s << "------------------------------------------------------------" << endl; - ExtendedConverterData extendedConverters = getExtendedConverters(); - if (!extendedConverters.isEmpty()) - s << "// Extended Converters" << endl; - foreach (const TypeEntry* externalType, extendedConverters.keys()) { - writeExtendedIsConvertibleFunction(s, externalType, extendedConverters[externalType]); - writeExtendedToCppFunction(s, externalType, extendedConverters[externalType]); - s << endl; - } - 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 << "extern \"C\" SBK_EXPORT_MODULE void init" << moduleName() << "()" << endl; - s << '{' << endl; - - // module inject-code target/beginning - if (!snips.isEmpty()) { - writeCodeSnips(s, snips, CodeSnip::Beginning, TypeSystem::TargetLangCode); - s << endl; - } - - foreach (const QString& requiredModule, TypeDatabase::instance()->requiredTargetImports()) { - s << INDENT << "if (!Shiboken::importModule(\"" << requiredModule << "\", &" << cppApiVariableName(requiredModule) << ")) {" << endl; - s << INDENT << INDENT << "PyErr_SetString(PyExc_ImportError," << "\"could not import "; - s << requiredModule << "\");" << endl << INDENT << INDENT << "return;" << endl; - s << INDENT << "}" << endl << endl; - } - - s << INDENT << "Shiboken::initShiboken();" << endl; - s << INDENT << "PyObject* module = Py_InitModule(\"" << moduleName() << "\", "; - s << moduleName() << "_methods);" << endl << endl; - - s << INDENT << "// Create a CObject containing the API pointer array's address" << endl; - s << INDENT << "static PyTypeObject* cppApi[" << "SBK_" << moduleName() << "_IDX_COUNT" << "];" << endl; - s << INDENT << cppApiVariableName() << " = cppApi;" << endl; - s << INDENT << "PyObject* cppApiObject = PyCObject_FromVoidPtr(reinterpret_cast(cppApi), 0);" << endl; - s << INDENT << "PyModule_AddObject(module, \"_Cpp_Api\", cppApiObject);" << endl << endl; - s << INDENT << "// Initialize classes in the type system" << endl; - s << classPythonDefines; - - if (!extendedConverters.isEmpty()) { - s << INDENT << "// Initialize extended Converters" << endl; - s << INDENT << "Shiboken::SbkBaseWrapperType* shiboType;" << endl << endl; - } - foreach (const TypeEntry* externalType, extendedConverters.keys()) { - writeExtendedConverterInitialization(s, externalType, extendedConverters[externalType]); - s << endl; - } - s << endl; - - if (!globalEnums().isEmpty()) { - s << INDENT << "// Initialize enums" << endl; - s << INDENT << "PyObject* enum_item;" << endl << endl; - } - - foreach (const AbstractMetaEnum* cppEnum, globalEnums()) - writeEnumInitialization(s, cppEnum); - - // Register primitive types on TypeResolver - s << INDENT << "// Register primitive types on TypeResolver" << endl; - foreach(const PrimitiveTypeEntry* pte, primitiveTypes()) { - if (pte->generateCode()) - s << INDENT << "Shiboken::TypeResolver::createValueTypeResolver<" << pte->name() << " >(\"" << pte->name() << "\");" << endl; - } - // 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); - typeResolvers << QMetaObject::normalizedType(value.toAscii().constData()); - } - } - } - } - } - foreach (QString type, typeResolvers) - s << INDENT << "Shiboken::TypeResolver::createValueTypeResolver<" << type << " >(\"" << type << "\");" << 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; - } - - s << '}' << endl << endl; - - // module inject-code native/end - if (!snips.isEmpty()) { - writeCodeSnips(s, snips, CodeSnip::End, TypeSystem::NativeCode); - s << endl; - } - } -} - -bool CppGenerator::writeParentChildManagement(QTextStream& s, const AbstractMetaFunction* func, int argIndex, bool useHeuristicPolicy) -{ - const int numArgs = func->arguments().count(); - const AbstractMetaClass* cppClass = func->ownerClass(); - const AbstractMetaClass* dClass = func->declaringClass(); - bool ctorHeuristicEnabled = func->isConstructor() && useCtorHeuristic() && useHeuristicPolicy; - - QString parentVariable; - QString childVariable; - ArgumentOwner argOwner = func->argumentOwner(cppClass, argIndex); - - if (argOwner.index == -2) //invalid - argOwner = func->argumentOwner(dClass, argIndex); - - bool usePyArgs = pythonFunctionWrapperUsesListOfArguments(OverloadData(getFunctionGroups(func->implementingClass())[func->name()], this)); - - 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" && (arg->type()->isObject() || arg->type()->isQObject())) { - action = ArgumentOwner::Add; - parentIndex = argIndex; - childIndex = -1; - } - } - - 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 = "self"; - else - parentVariable = usePyArgs ? "pyargs["+QString::number(parentIndex-1)+"]" : "arg"; - } - - if (childIndex == 0) - childVariable = PYTHON_RETURN_VAR; - else if (childIndex == -1) - childVariable = "self"; - else - childVariable = usePyArgs ? "pyargs["+QString::number(childIndex-1)+"]" : "arg"; - - s << INDENT << "Shiboken::setParent(" << parentVariable << ", " << childVariable << ");\n"; - - return true; - } - - if (argIndex == 0 && useHeuristicPolicy) - writeReturnValueHeuristics(s, func); - - 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, i == 0 ? useHeuristicForReturn : true); -} - -void CppGenerator::writeReturnValueHeuristics(QTextStream& s, const AbstractMetaFunction* func, const QString& self) -{ - AbstractMetaType *type = func->type(); - if (!useReturnValueHeuristic() - || !func->ownerClass() - || !type - || func->isStatic() - || !func->typeReplaced(0).isEmpty()) { - return; - } - - if (type->isQObject() || type->isObject() || type->isValuePointer()) - s << INDENT << "Shiboken::setParent(" << self << ", " PYTHON_RETURN_VAR ");" << endl; -} - -void CppGenerator::writeHashFunction(QTextStream& s, const AbstractMetaClass* metaClass) -{ - s << "static long " << cpythonBaseName(metaClass) << "_HashFunc(PyObject* obj)"; - s << '{' << endl; - s << INDENT << "return " << metaClass->typeEntry()->hashFunction() << '('; - writeToCppConversion(s, metaClass, "obj"); - s << ");" << endl; - s << '}' << endl << endl; -} - -void CppGenerator::writeObjCopierFunction(QTextStream& s, const AbstractMetaClass* metaClass) -{ - if (!(metaClass->typeEntry()->isValue() && shouldGenerateCppWrapper(metaClass))) - return; - s << "static void* " << cpythonBaseName(metaClass) << "_ObjCopierFunc(const void* ptr)"; - s << '{' << endl; - s << INDENT << "return new " << wrapperName(metaClass) << "(*reinterpret_castqualifiedCppName() << "*>(ptr));\n"; - s << '}' << endl << endl; - -} - -void CppGenerator::writeStdListWrapperMethods(QTextStream& s, const AbstractMetaClass* metaClass) -{ - //len - s << "Py_ssize_t " << cpythonBaseName(metaClass->typeEntry()) << "__len__" << "(PyObject* self)" << endl << '{' << endl; - s << INDENT << "if (Shiboken::cppObjectIsInvalid(self))" << endl; - s << INDENT << INDENT << "return 0;" << endl << endl; - s << INDENT << metaClass->qualifiedCppName() << " &cppSelf = Shiboken::Converter<" << metaClass->qualifiedCppName() <<"& >::toCpp(self);" << endl; - s << INDENT << "return cppSelf.size();" << endl; - s << "}" << endl; - - //getitem - s << "PyObject* " << cpythonBaseName(metaClass->typeEntry()) << "__getitem__" << "(PyObject* self, Py_ssize_t _i)" << endl << '{' << endl; - s << INDENT << "if (Shiboken::cppObjectIsInvalid(self))" << endl; - s << INDENT << INDENT << "return 0;" << endl << endl; - s << INDENT << metaClass->qualifiedCppName() << " &cppSelf = Shiboken::Converter<" << metaClass->qualifiedCppName() <<"& >::toCpp(self);" << endl; - s << INDENT << "if (_i < 0 || _i >= (Py_ssize_t) cppSelf.size()) {" << endl; - s << INDENT << INDENT << "PyErr_SetString(PyExc_IndexError, \"index out of bounds\");" << endl; - s << INDENT << INDENT << "return 0;" << endl << INDENT << "}" << endl; - s << INDENT << metaClass->qualifiedCppName() << "::iterator _item = cppSelf.begin();" << endl; - s << INDENT << "for(Py_ssize_t pos=0; pos < _i; pos++) _item++;" << endl; - s << INDENT << "return Shiboken::Converter<" << metaClass->qualifiedCppName() << "::value_type>::toPython(*_item);" << endl; - s << "}" << endl; - - //setitem - s << "int " << cpythonBaseName(metaClass->typeEntry()) << "__setitem__" << "(PyObject* self, Py_ssize_t _i, PyObject* _value)" << endl << '{' << endl; - s << INDENT << "if (Shiboken::cppObjectIsInvalid(self))" << endl; - s << INDENT << INDENT << "return -1;" << endl; - s << INDENT << metaClass->qualifiedCppName() << " &cppSelf = Shiboken::Converter<" << metaClass->qualifiedCppName() <<"& >::toCpp(self);" << endl; - s << INDENT << "if (_i < 0 || _i >= (Py_ssize_t) cppSelf.size()) {" << endl; - s << INDENT << INDENT << "PyErr_SetString(PyExc_IndexError, \"list assignment index out of range\");" << endl; - s << INDENT << INDENT << "return -1;" << endl << INDENT << "}" << endl; - s << INDENT << metaClass->qualifiedCppName() << "::iterator _item = cppSelf.begin();" << endl; - s << INDENT << "for(Py_ssize_t pos=0; pos < _i; pos++) _item++;" << endl; - - s << INDENT << metaClass->qualifiedCppName() << "::value_type cppValue = Shiboken::Converter<" << metaClass->qualifiedCppName() << "::value_type>::toCpp(_value);" << endl; - s << INDENT << "*_item = cppValue;" << endl; - s << INDENT << "return 0;"; - s << endl << "}" << endl; -} - - diff --git a/cppgenerator.h b/cppgenerator.h deleted file mode 100644 index b4d68dc45..000000000 --- a/cppgenerator.h +++ /dev/null @@ -1,201 +0,0 @@ -/* - * 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); - void writeVirtualMethodNative(QTextStream& s, const AbstractMetaFunction* func); - void writeMetaObjectMethod(QTextStream& s, const AbstractMetaClass* metaClass); - - void writeConstructorWrapper(QTextStream &s, const AbstractMetaFunctionList overloads); - void writeDestructorWrapper(QTextStream& s, const AbstractMetaClass* metaClass); - void writeMinimalConstructorCallArguments(QTextStream& s, const AbstractMetaClass* metaClass); - void writeMinimalConstructorCallArguments(QTextStream& s, const AbstractMetaType* type); - void writeMethodWrapper(QTextStream &s, const AbstractMetaFunctionList overloads); - void writeArgumentsInitializer(QTextStream& s, OverloadData& overloadData); - void writeCppSelfDefinition(QTextStream& s, const AbstractMetaFunction* func); - - void writeErrorSection(QTextStream& s, OverloadData& overloadData); - /** - * Writes the check section for the validity of wrapped C++ objects. - * \param s text stream to write - * \param argName Python argument name - * \param type the TypeEntry passed when the validity check must confirm the type of the Python wrapper to be checked - */ - void writeInvalidCppObjectCheck(QTextStream& s, QString pyArgName = "self", const TypeEntry* type = 0); - void writeTypeCheck(QTextStream& s, const AbstractMetaType* argType, QString argumentName, bool isNumber = false, QString customType = ""); - 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 - */ - void writeArgumentConversion(QTextStream& s, const AbstractMetaType* argType, - QString argName, QString pyArgName, - const AbstractMetaClass* context = 0, - QString defaultValue = QString()); - /// Convenience method to call writeArgumentConversion with an AbstractMetaArgument instead of an AbstractMetaType. - void writeArgumentConversion(QTextStream& s, const AbstractMetaArgument* arg, - QString argName, QString pyArgName, - const AbstractMetaClass* context = 0, - QString defaultValue = QString()) - { - writeArgumentConversion(s, arg->type(), argName, pyArgName, context, defaultValue); - } - - /** - * 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 = 0); - 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); - /// Writes the struct PySequenceMethods for types thats supports the python sequence protocol - void writeTypeAsSequenceDefinition(QTextStream& s, const AbstractMetaClass* metaClass); - void writeTypeAsNumberDefinition(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 writeEnumNewMethod(QTextStream& s, const AbstractMetaEnum* cppEnum); - void writeEnumDefinition(QTextStream& s, const AbstractMetaEnum* metaEnum); - void writeEnumInitialization(QTextStream& s, const AbstractMetaEnum* metaEnum); - - void writeSignalInitialization(QTextStream& s, const AbstractMetaClass* metaClass); - - void writeFlagsDefinition(QTextStream& s, const AbstractMetaEnum* cppEnum); - void writeFlagsMethods(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 mulltiple inheritance. - void writeSpecialCastFunction(QTextStream& s, const AbstractMetaClass* metaClass); - - void writeExtendedIsConvertibleFunction(QTextStream& s, const TypeEntry* externalType, const QList& conversions); - void writeExtendedToCppFunction(QTextStream& s, const TypeEntry* externalType, const QList& conversions); - 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 = "self"); - /** - * 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 casted. - QStringList getAncestorMultipleInheritance(const AbstractMetaClass* metaClass); - - /// Returns true if the given class supports the python sequence protocol - bool supportsSequenceProtocol(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); - void writeObjCopierFunction(QTextStream& s, const AbstractMetaClass* metaClass); - - // Write default implementations for sequence protocol - void writeStdListWrapperMethods(QTextStream& s, const AbstractMetaClass* metaClass); - - // Maps special function names to function parameters and return types - // used by CPython API in the sequence protocol. - QHash > m_sequenceProtocol; - int m_currentErrorCode; -}; - -#endif // CPPGENERATOR_H - diff --git a/generator/cppgenerator.cpp b/generator/cppgenerator.cpp new file mode 100644 index 000000000..2df2bae92 --- /dev/null +++ b/generator/cppgenerator.cpp @@ -0,0 +1,3849 @@ +/* + * 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 + * + */ + +#include "cppgenerator.h" +#include +#include + +#include +#include +#include + +inline CodeSnipList getConversionRule(TypeSystem::Language lang, const AbstractMetaFunction *function) +{ + CodeSnipList list; + + foreach(AbstractMetaArgument *arg, function->arguments()) { + QString convRule = function->conversionRule(lang, arg->argumentIndex() + 1); + if (!convRule.isEmpty()) { + CodeSnip snip(0, TypeSystem::TargetLangCode); + snip.position = CodeSnip::Beginning; + + convRule.replace("%in", arg->name()); + convRule.replace("%out", arg->name() + "_out"); + + snip.addCode(convRule); + list << snip; + } + + } + return list; +} + +// utiliy functions +inline CodeSnipList getReturnConversionRule(TypeSystem::Language lang, + const AbstractMetaFunction *function, + const QString& inputName, + const QString& outputName) +{ + CodeSnipList list; + + QString convRule = function->conversionRule(lang, 0); + if (!convRule.isEmpty()) { + CodeSnip snip(0, lang); + snip.position = CodeSnip::Beginning; + + convRule.replace("%in", inputName); + convRule.replace("%out", outputName); + + snip.addCode(convRule); + list << snip; + } + + return list; +} + + +CppGenerator::CppGenerator() : m_currentErrorCode(0) +{ + // sequence protocol functions + typedef QPair StrPair; + m_sequenceProtocol.insert("__len__", StrPair("PyObject* self", "Py_ssize_t")); + m_sequenceProtocol.insert("__getitem__", StrPair("PyObject* self, Py_ssize_t _i", "PyObject*")); + m_sequenceProtocol.insert("__setitem__", StrPair("PyObject* self, Py_ssize_t _i, PyObject* _value", "int")); + m_sequenceProtocol.insert("__getslice__", StrPair("PyObject* self, Py_ssize_t _i1, Py_ssize_t _i2", "PyObject*")); + m_sequenceProtocol.insert("__setslice__", StrPair("PyObject* self, Py_ssize_t _i1, Py_ssize_t _i2, PyObject* _value", "int")); + m_sequenceProtocol.insert("__contains__", StrPair("PyObject* self, PyObject* _value", "int")); + m_sequenceProtocol.insert("__concat__", StrPair("PyObject* self, PyObject* _other", "PyObject*")); +} + +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(); +} + +/*! + 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; + +#ifndef AVOID_PROTECTED_HACK + if (!metaClass->isNamespace() && !metaClass->hasPrivateDestructor()) { + s << "//workaround to access protected functions" << endl; + s << "#define protected public" << endl << endl; + } +#endif + + // headers + s << "// default includes" << endl; + s << "#include " << endl; + if (usePySideExtensions()) { + s << "#include " << endl; + s << "#include " << endl; + s << "#include " << endl; + } + + s << "#include \n"; + s << "#include \n"; + if (usePySideExtensions()) { + if (metaClass->isQObject()) { + s << "#include \n"; + s << "#include \n"; + } + } + + // 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; + } + } + + //Extra includes + s << endl << "// Extra includes" << endl; + QList includes = metaClass->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; + + s << "using namespace Shiboken;" << 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, 0, 0, metaClass); + s << endl; + } + + if (shouldGenerateCppWrapper(metaClass)) { + s << "// Native ---------------------------------------------------------" << endl; + s << endl; + + 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); +#ifdef AVOID_PROTECTED_HACK + else if (!metaClass->hasPrivateDestructor() && (func->isVirtual() || func->isAbstract())) +#else + else if (func->isVirtual() || func->isAbstract()) +#endif + writeVirtualMethodNative(s, func); + } + +#ifdef AVOID_PROTECTED_HACK + if (!metaClass->hasPrivateDestructor()) { +#endif + + if (usePySideExtensions() && metaClass->isQObject()) + writeMetaObjectMethod(s, metaClass); + + writeDestructorNative(s, metaClass); + +#ifdef AVOID_PROTECTED_HACK + } +#endif + } + + Indentation indentation(INDENT); + + QString methodsDefinitions; + QTextStream md(&methodsDefinitions); + QString singleMethodDefinitions; + QTextStream smd(&singleMethodDefinitions); + + bool hasComparisonOperator = metaClass->hasComparisonOperatorOverload(); + bool typeAsNumber = metaClass->hasArithmeticOperatorOverload() || metaClass->hasLogicalOperatorOverload() || metaClass->hasBitwiseOperatorOverload(); + + 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()) + overloads.append(func); + } + + if (overloads.isEmpty()) + continue; + + const AbstractMetaFunction* rfunc = overloads.first(); + if (m_sequenceProtocol.contains(rfunc->name())) + continue; + + if (rfunc->isConstructor()) + writeConstructorWrapper(s, overloads); + + if (!rfunc->isConstructor() && !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$"), ""); + + // Write single method definitions + s << singleMethodDefinitions; + + // Write methods definition + s << "static PyMethodDef " << className << "_methods[] = {" << endl; + s << methodsDefinitions << 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 (typeAsNumber) { + 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); + } + + s << "// type has number operators" << endl; + writeTypeAsNumberDefinition(s, metaClass); + } + + if (supportsSequenceProtocol(metaClass)) { + writeSequenceMethods(s, metaClass); + writeTypeAsSequenceDefinition(s, metaClass); + } + + if (hasComparisonOperator) { + 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); + writeObjCopierFunction(s, metaClass); + writeClassDefinition(s, metaClass); + s << endl; + + if (metaClass->isPolymorphic() && metaClass->baseClass()) + writeTypeDiscoveryFunction(s, metaClass); + + + foreach (AbstractMetaEnum* cppEnum, metaClass->enums()) { + if (cppEnum->isAnonymous()) + continue; + + bool hasFlags = cppEnum->typeEntry()->flags(); + if (hasFlags) { + writeFlagsMethods(s, cppEnum); + writeFlagsNumberMethodsDefinition(s, cppEnum); + s << endl; + } + + writeEnumDefinition(s, cppEnum); + + if (hasFlags) { + // Write Enum as Flags definition (at the moment used only by QFlags) + writeFlagsDefinition(s, cppEnum); + s << endl; + } + } + s << endl; + + writeClassRegister(s, metaClass); + + // class inject-code native/end + if (!metaClass->typeEntry()->codeSnips().isEmpty()) { + writeCodeSnips(s, metaClass->typeEntry()->codeSnips(), CodeSnip::End, TypeSystem::NativeCode, 0, 0, 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); + if (usePySideExtensions() && func->ownerClass()->isQObject()) + s << ", m_metaObject(0)"; + + 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; + s << INDENT << "BindingManager::instance().invalidateWrapper(this);" << endl; + s << '}' << endl; +} + +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* type = func->type() ? func->type()->typeEntry() : 0; + + QString prefix = wrapperName(func->ownerClass()) + "::"; + s << functionSignature(func, prefix, "", Generator::SkipDefaultValues|Generator::OriginalTypeDescription) << endl; + s << "{" << endl; + + Indentation indentation(INDENT); + + if (func->isAbstract() && func->isModifiedRemoved()) { + ReportHandler::warning("Pure virtual method \"" + func->ownerClass()->name() + "::" + func->minimalSignature() + "\" must be implement but was completely removed on typesystem."); + s << INDENT << "return"; + if (func->type()) { + s << ' '; + writeMinimalConstructorCallArguments(s, func->type()); + } + s << ';' << endl; + s << '}' << endl << endl; + return; + } + + s << INDENT << "Shiboken::GilState gil;" << endl; + + s << INDENT << "Shiboken::AutoDecRef py_override(BindingManager::instance().getOverride(this, \""; + s << func->name() << "\"));" << endl; + + s << INDENT << "if (py_override.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() << '.' << func->name(); + s << "()' not implemented.\");" << endl; + s << INDENT << "return "; + if (func->type()) { + writeMinimalConstructorCallArguments(s, func->type()); + } + } else { + if (func->allowThread()) { + s << INDENT << "Shiboken::ThreadStateSaver " THREAD_STATE_SAVER_VAR ";" << endl; + s << INDENT << THREAD_STATE_SAVER_VAR ".save();" << endl; + } + + s << INDENT << "return this->" << func->implementingClass()->qualifiedCppName() << "::"; + writeFunctionCall(s, func, Generator::VirtualCall); + } + } + s << ';' << endl; + s << INDENT << '}' << endl << endl; + + CodeSnipList convRules = getConversionRule(TypeSystem::TargetLangCode, func); + if (convRules.size()) + writeCodeSnips(s, convRules, CodeSnip::Beginning, TypeSystem::TargetLangCode, func); + + s << INDENT << "Shiboken::AutoDecRef pyargs("; + + if (func->arguments().isEmpty()) { + 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); + bool convert = arg->type()->isObject() + || arg->type()->isQObject() + || arg->type()->isValue() + || arg->type()->isValuePointer() + || arg->type()->isNativePointer() + || arg->type()->isFlags() + || arg->type()->isEnum() + || arg->type()->isContainer() + || arg->type()->isReference() + || (arg->type()->isPrimitive() + && !m_formatUnits.contains(arg->type()->typeEntry()->name())); + + bool hasConversionRule = !func->conversionRule(TypeSystem::TargetLangCode, arg->argumentIndex() + 1).isEmpty(); + + Indentation indentation(INDENT); + ac << INDENT; + if (convert && !hasConversionRule) + writeToPythonConversion(ac, arg->type(), func->ownerClass()); + + if (hasConversionRule) { + ac << arg->name() << "_out"; + } else { + QString argName = arg->name(); +#ifdef AVOID_PROTECTED_HACK + const AbstractMetaEnum* metaEnum = findAbstractMetaEnum(arg->type()); + if (metaEnum && metaEnum->isProtected()) { + argName.prepend(protectedEnumSurrogateName(metaEnum) + '('); + argName.append(')'); + } +#endif + ac << (convert ? "(" : "") << argName << (convert ? ")" : ""); + } + + 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 invalidadeArg" << argMod.index << " = PyTuple_GET_ITEM(pyargs, " << 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(py_override, pyargs, NULL));" << endl; + if (type) { + if (invalidateReturn) + s << INDENT << "bool invalidadeArg0 = " PYTHON_RETURN_VAR "->ob_refcnt == 1;" << 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 "; + writeMinimalConstructorCallArguments(s, func->type()); + s << ';' << endl; + } + s << INDENT << '}' << endl; + + if (func->type()) { + s << INDENT << "// Check return type" << endl; + s << INDENT << "bool typeIsValid = "; + QString desiredType; + if (func->typeReplaced(0).isEmpty()) { + s << cpythonIsConvertibleFunction(func->type()); + // SbkType would return null when the type is a container. + if (func->type()->typeEntry()->isContainer()) { + desiredType = '"' + reinterpret_cast(func->type()->typeEntry())->typeName() + '"'; + } else { + QString typeName = func->type()->cppSignature(); +#ifdef AVOID_PROTECTED_HACK + const AbstractMetaEnum* metaEnum = findAbstractMetaEnum(func->type()); + if (metaEnum && metaEnum->isProtected()) + typeName = protectedEnumSurrogateName(metaEnum); +#endif + if (func->type()->isPrimitive()) + desiredType = "\"" + func->type()->name() + "\""; + else + desiredType = "SbkType<" + typeName + " >()->tp_name"; + } + } else { + s << guessCPythonCheckFunction(func->typeReplaced(0)); + desiredType = '"' + func->typeReplaced(0) + '"'; + } + s << "(" PYTHON_RETURN_VAR ");" << endl; + if (func->type()->isQObject() || func->type()->isObject() || func->type()->isValuePointer()) + s << INDENT << "typeIsValid = typeIsValid || (" PYTHON_RETURN_VAR " == Py_None);" << endl; + + s << INDENT << "if (!typeIsValid) {" << endl; + { + Indentation indent(INDENT); + s << INDENT << "PyErr_Format(PyExc_TypeError, \"Invalid return value in function %s, expected %s, got %s.\", \"" + << func->ownerClass()->name() << '.' << func->name() << "\", " << desiredType + << ", " PYTHON_RETURN_VAR "->ob_type->tp_name);" << endl; + s << INDENT << "return "; + writeMinimalConstructorCallArguments(s, func->type()); + s << ';' << endl; + } + s << INDENT << "}" << endl; + } + + bool hasConversionRule = !func->conversionRule(TypeSystem::NativeCode, 0).isEmpty(); + if (hasConversionRule) { + CodeSnipList convRule = getReturnConversionRule(TypeSystem::NativeCode, func, "", CPP_RETURN_VAR); + writeCodeSnips(s, convRule, CodeSnip::Any, TypeSystem::NativeCode, func); + } else if (!injectedCodeHasReturnValueAttribution(func, TypeSystem::NativeCode)) { + s << INDENT; +#ifdef AVOID_PROTECTED_HACK + QString enumName; + const AbstractMetaEnum* metaEnum = findAbstractMetaEnum(func->type()); + bool isProtectedEnum = metaEnum && metaEnum->isProtected(); + if (isProtectedEnum) { + enumName = metaEnum->name(); + if (metaEnum->enclosingClass()) + enumName = metaEnum->enclosingClass()->qualifiedCppName() + "::" + enumName; + s << enumName; + } else +#endif + s << translateTypeForWrapperMethod(func->type(), func->implementingClass()); + s << " " CPP_RETURN_VAR "("; +#ifdef AVOID_PROTECTED_HACK + if (isProtectedEnum) + s << enumName << '('; +#endif + writeToCppConversion(s, func->type(), func->implementingClass(), PYTHON_RETURN_VAR); +#ifdef AVOID_PROTECTED_HACK + if (isProtectedEnum) + s << ')'; +#endif + s << ')'; + s << ';' << endl; + } + } + } + + if (invalidateReturn) { + s << INDENT << "if (invalidadeArg0)" << endl; + Indentation indentation(INDENT); + s << INDENT << "BindingManager::instance().invalidateWrapper(" << PYTHON_RETURN_VAR ".object());" << endl; + } + + foreach (FunctionModification funcMod, func->modifications()) { + foreach (ArgumentModification argMod, funcMod.argument_mods) { + if (argMod.resetAfterUse) { + s << INDENT << "if (invalidadeArg" << argMod.index << ")" << endl; + Indentation indentation(INDENT); + s << INDENT << "BindingManager::instance().invalidateWrapper(PyTuple_GET_ITEM(pyargs, "; + s << (argMod.index - 1) << "));" << 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 (type) { + if (!invalidateReturn && (func->type()->isObject() || func->type()->isValuePointer()) ) { + s << INDENT << "if (" << PYTHON_RETURN_VAR << "->ob_refcnt < 2) {" << endl; + { + Indentation indent(INDENT); + s << INDENT << "PyErr_SetString(PyExc_ReferenceError, \"Returning last python reference on virutal function: " + << func->ownerClass()->name() << "." << func->name() << "\");" << endl; + s << INDENT << "PyErr_Print();" << endl; + s << INDENT << "assert(false);" << endl; + } + s << INDENT << "}" << endl; + } + s << INDENT << "return " CPP_RETURN_VAR ";" << endl; + } + + s << '}' << endl << endl; +} + +void CppGenerator::writeMetaObjectMethod(QTextStream& s, const AbstractMetaClass* metaClass) +{ + Indentation indentation(INDENT); + QString wrapperClassName = wrapperName(metaClass); + QString prefix = wrapperClassName + "::"; + s << "const QMetaObject* " << wrapperClassName << "::metaObject() const\n{\n"; + s << INDENT << "if (!m_metaObject) {\n"; + { + Indentation indentation(INDENT); + s << INDENT << "PyObject *pySelf = BindingManager::instance().retrieveWrapper(this);\n" + << INDENT << "void *typeData = Shiboken::getTypeUserData(reinterpret_cast(pySelf));" << endl + << INDENT << "if (!typeData) {" << endl; + { + Indentation indentation2(INDENT); + s << INDENT << "m_metaObject = PySide::DynamicQMetaObject::createBasedOn(pySelf, pySelf->ob_type, &" + << metaClass->qualifiedCppName() << "::staticMetaObject);" << endl + << INDENT << "Shiboken::setTypeUserData(reinterpret_cast(pySelf), m_metaObject, PySide::deleteDynamicQMetaObject);" << endl; + } + s << INDENT << "} else {" << endl; + { + Indentation indentation2(INDENT); + s << INDENT << "m_metaObject = reinterpret_cast(typeData);" << endl; + } + s << INDENT << "}" << endl; + } + s << INDENT << "}" << endl; + s << INDENT << "return m_metaObject;\n"; + s << "}\n\n"; + + s << "int " << wrapperClassName << "::qt_metacall(QMetaObject::Call call, int id, void** args)\n"; + s << "{\n"; + s << INDENT << "int result = " << metaClass->qualifiedCppName() << "::qt_metacall(call, id, args);\n"; + s << INDENT << "return result < 0 ? result : PySide::SignalManager::qt_metacall(this, call, id, args);\n"; + s << "}\n\n"; +} + +void CppGenerator::writeConstructorWrapper(QTextStream& s, const AbstractMetaFunctionList overloads) +{ + OverloadData overloadData(overloads, this); + + const AbstractMetaFunction* rfunc = overloadData.referenceFunction(); + const AbstractMetaClass* metaClass = rfunc->ownerClass(); + QString className = cpythonTypeName(metaClass); + + m_currentErrorCode = -1; + + s << "static int" << endl; + s << cpythonFunctionName(rfunc) << "(PyObject* self, PyObject* args, PyObject* kwds)" << endl; + s << '{' << endl; + + // Check if the right constructor was called. + if (!metaClass->hasPrivateDestructor()) { + s << INDENT << "if (Shiboken::isUserType(self) && !Shiboken::canCallConstructor(self->ob_type, Shiboken::SbkType<" << metaClass->qualifiedCppName() << " >()))" << endl; + Indentation indent(INDENT); + s << INDENT << "return " << m_currentErrorCode << ';' << endl << endl; + } + + s << INDENT; + bool hasCppWrapper = shouldGenerateCppWrapper(metaClass); + s << (hasCppWrapper ? wrapperName(metaClass) : metaClass->qualifiedCppName()); + s << "* cptr = 0;" << endl; + + bool needsOverloadId = overloadData.maxArgs() > 0; + if (needsOverloadId) + s << INDENT << "int overloadId = -1;" << 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 << "SbkBaseWrapper* sbkSelf = reinterpret_cast(self);" << endl; + + if (metaClass->isAbstract() || metaClass->baseClassNames().size() > 1) { + s << INDENT << "SbkBaseWrapperType* type = reinterpret_cast(self->ob_type);" << endl; + s << INDENT << "SbkBaseWrapperType* 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 << "type->mi_init = myType->mi_init;" << endl; + s << INDENT << "type->mi_offsets = myType->mi_offsets;" << endl; + s << INDENT << "type->mi_specialcast = myType->mi_specialcast;" << endl; + } + if (!metaClass->isAbstract()) + s << INDENT << '}' << endl << endl; + } + + s << endl; + + if (!metaClass->isQObject() && overloadData.hasArgumentWithDefaultValue()) + s << INDENT << "int numNamedArgs = (kwds ? PyDict_Size(kwds) : 0);" << endl; + if (overloadData.maxArgs() > 0) { + s << endl << INDENT << "int numArgs = "; + writeArgumentsInitializer(s, overloadData); + } + + if (needsOverloadId) + writeOverloadedFunctionDecisor(s, overloadData); + + writeFunctionCalls(s, overloadData); + s << endl; + + s << INDENT << "if (PyErr_Occurred() || !Shiboken::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 << "sbkSelf->validCppObject = 1;" << 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 << "sbkSelf->containsCppWrapper = 1;" << endl; + s << INDENT << "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::signalUpdateSource(self);" << endl; + s << INDENT << "metaObject = cptr->metaObject(); // <- init python qt properties" << endl; + s << INDENT << "if (kwds && !PySide::fillQtProperties(self, metaObject, kwds, argNames, " << argNamesSet.count() << "))" << endl; + { + Indentation indentation(INDENT); + s << INDENT << "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; + m_currentErrorCode = 0; +} + +void CppGenerator::writeMinimalConstructorCallArguments(QTextStream& s, const AbstractMetaClass* metaClass) +{ + if (!metaClass) + return; + + AbstractMetaFunctionList ctors = metaClass->queryFunctions(AbstractMetaClass::Constructors); + const AbstractMetaFunction* ctor = 0; + + foreach (const AbstractMetaFunction* candidate, ctors) { + if (candidate->arguments().size() == 0) { + ctor = candidate; + break; + } + + bool allPrimitives = true; + foreach (const AbstractMetaArgument* arg, candidate->arguments()) { + if (!arg->type()->isPrimitive() && arg->defaultValueExpression().isEmpty()) { + allPrimitives = false; + break; + } + } + if (allPrimitives) { + if (!ctor || candidate->arguments().size() < ctor->arguments().size()) + ctor = candidate; + } + } + + if (!ctor) { + ReportHandler::warning("Class "+metaClass->name()+" does not have a default constructor."); + return; + } + + QStringList argValues; + AbstractMetaArgumentList args = ctor->arguments(); + for (int i = 0; i < args.size(); i++) { + if (args[i]->defaultValueExpression().isEmpty()) + argValues << args[i]->type()->name()+"(0)"; + } + s << metaClass->qualifiedCppName() << '(' << argValues.join(QLatin1String(", ")) << ')'; +} + +void CppGenerator::writeMinimalConstructorCallArguments(QTextStream& s, const AbstractMetaType* metaType) +{ + Q_ASSERT(metaType); + const TypeEntry* type = metaType->typeEntry(); + + if (type->isObject() || metaType->isValuePointer()) { + s << "0"; + } else if (type->isPrimitive()) { + const PrimitiveTypeEntry* primitiveTypeEntry = reinterpret_cast(type); + if (primitiveTypeEntry->hasDefaultConstructor()) + s << primitiveTypeEntry->defaultConstructor(); + else + s << type->name() << "(0)"; + } else if (type->isContainer() || type->isFlags() || type->isEnum()){ + s << metaType->cppSignature() << "()"; + } else if (metaType->isNativePointer() && type->isVoid()) { + s << "0"; + } else { + // this is slowwwww, FIXME: Fix the API od APIExtractor, these things should be easy! + foreach (AbstractMetaClass* metaClass, classes()) { + if (metaClass->typeEntry() == type) { + writeMinimalConstructorCallArguments(s, metaClass); + return; + } + } + ReportHandler::warning("Could not find a AbstractMetaClass for type "+metaType->name()); + } +} + +void CppGenerator::writeMethodWrapper(QTextStream& s, const AbstractMetaFunctionList overloads) +{ + OverloadData overloadData(overloads, this); + const AbstractMetaFunction* rfunc = overloadData.referenceFunction(); + + //DEBUG +// if (rfunc->name() == "operator+" && rfunc->ownerClass()->name() == "Str") { +// QString dumpFile = QString("/tmp/%1_%2.dot").arg(moduleName()).arg(pythonOperatorFunctionName(rfunc)).toLower(); +// overloadData.dumpGraph(dumpFile); +// } + //DEBUG + + int minArgs = overloadData.minArgs(); + int maxArgs = overloadData.maxArgs(); + bool usePyArgs = pythonFunctionWrapperUsesListOfArguments(overloadData); + bool usesNamedArguments = overloadData.hasArgumentWithDefaultValue(); + + s << "static PyObject* "; + s << cpythonFunctionName(rfunc) << "(PyObject* self"; + if (maxArgs > 0) { + s << ", PyObject* arg"; + if (usePyArgs) + s << 's'; + if (usesNamedArguments) + s << ", PyObject* kwds"; + } + s << ')' << endl << '{' << endl; + + if (rfunc->implementingClass() && + (!rfunc->implementingClass()->isNamespace() && overloadData.hasInstanceFunction())) { + + s << INDENT; +#ifdef AVOID_PROTECTED_HACK + QString _wrapperName = wrapperName(rfunc->ownerClass()); + bool hasProtectedMembers = rfunc->ownerClass()->hasProtectedMembers(); + s << (hasProtectedMembers ? _wrapperName : rfunc->ownerClass()->qualifiedCppName()); +#else + s << rfunc->ownerClass()->qualifiedCppName(); +#endif + s << "* " CPP_SELF_VAR " = 0;" << endl; + + if (rfunc->isOperatorOverload() && rfunc->isBinaryOperator()) { + QString checkFunc = cpythonCheckFunction(rfunc->ownerClass()->typeEntry()); + s << INDENT << "bool isReverse = " << checkFunc << "(arg) && !" << checkFunc << "(self);\n" + << INDENT << "if (isReverse)\n"; + Indentation indent(INDENT); + s << INDENT << "std::swap(self, arg);\n\n"; + } + + // Sets the C++ "self" (the "this" for the object) if it has one. + QString cppSelfAttribution = CPP_SELF_VAR " = "; +#ifdef AVOID_PROTECTED_HACK + cppSelfAttribution += (hasProtectedMembers ? QString("(%1*)").arg(_wrapperName) : ""); +#endif + cppSelfAttribution += cpythonWrapperCPtr(rfunc->ownerClass(), "self"); + + // Checks if the underlying C++ object is valid. + if (overloadData.hasStaticFunction()) { + s << INDENT << "if (self) {" << endl; + { + Indentation indent(INDENT); + writeInvalidCppObjectCheck(s); + s << INDENT << cppSelfAttribution << ';' << endl; + } + s << INDENT << '}' << endl; + } else { + writeInvalidCppObjectCheck(s); + s << INDENT << cppSelfAttribution << ';' << endl; + } + s << endl; + } + + bool hasReturnValue = overloadData.hasNonVoidReturnType(); + + if (hasReturnValue && !rfunc->isInplaceOperator()) + s << INDENT << "PyObject* " PYTHON_RETURN_VAR " = 0;" << endl; + + bool needsOverloadId = overloadData.maxArgs() > 0; + if (needsOverloadId) + s << INDENT << "int overloadId = -1;" << endl; + + if (usesNamedArguments) { + s << INDENT << "int numNamedArgs = (kwds ? PyDict_Size(kwds) : 0);" << endl; + } + + if (minArgs != maxArgs || maxArgs > 1) { + s << INDENT << "int numArgs = "; + if (minArgs == 0 && maxArgs == 1 && !usePyArgs) + s << "(arg == 0 ? 0 : 1);" << endl; + else + writeArgumentsInitializer(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 callExtendedReverseOperator = hasReturnValue && !rfunc->isInplaceOperator() && 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 << "&& SbkBaseWrapper_Check(arg)" << endl; + s << INDENT << "&& !PyObject_TypeCheck(arg, self->ob_type)" << endl; + s << INDENT << "&& PyObject_HasAttrString(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(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\"), self);" << 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 (needsOverloadId) + writeOverloadedFunctionDecisor(s, overloadData); + + writeFunctionCalls(s, overloadData); + s << endl; + + if (callExtendedReverseOperator) + s << endl << INDENT << "} // End of \"if (!" PYTHON_RETURN_VAR ")\"" << endl << endl; + + s << endl << INDENT << "if (PyErr_Occurred()"; + if (hasReturnValue && !rfunc->isInplaceOperator()) + s << " || !" PYTHON_RETURN_VAR; + s << ") {" << endl; + { + Indentation indent(INDENT); + if (hasReturnValue && !rfunc->isInplaceOperator()) + s << INDENT << "Py_XDECREF(" PYTHON_RETURN_VAR ");" << endl; + s << INDENT << "return " << m_currentErrorCode << ';' << endl; + } + s << INDENT << '}' << endl; + + if (hasReturnValue) { + if (rfunc->isInplaceOperator()) { + s << INDENT << "Py_INCREF(self);\n"; + s << INDENT << "return self;\n"; + } else { + s << INDENT << "return " PYTHON_RETURN_VAR ";\n"; + } + } else { + s << INDENT << "Py_RETURN_NONE;" << 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(); + + QStringList palist; + + s << INDENT << "PyObject* "; + if (!pythonFunctionWrapperUsesListOfArguments(overloadData)) { + s << "arg = 0"; + palist << "&arg"; + } else { + s << "pyargs[] = {" << QString(maxArgs, '0').split("", QString::SkipEmptyParts).join(", ") << '}'; + for (int i = 0; i < maxArgs; i++) + palist << QString("&(pyargs[%1])").arg(i); + } + s << ';' << endl << endl; + + QString pyargs = palist.join(", "); + + 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 << "pyargs[" << maxArgs << "] = PyTuple_GetSlice(args, " << maxArgs << ", numArgs);" << endl; + s << INDENT << "Shiboken::AutoDecRef auto_varargs(pyargs[" << 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(); + + if (usesNamedArguments) { + s << INDENT << "if (!PyArg_ParseTuple(" << (overloadData.hasVarargs() ? "nonvarargs" : "args"); + s << ", \"|" << QByteArray(maxArgs, 'O') << ':' << funcName << "\", " << pyargs << "))" << endl; + } else { + s << INDENT << "if (!PyArg_UnpackTuple(" << (overloadData.hasVarargs() ? "nonvarargs" : "args"); + s << ", \"" << funcName << "\", " << minArgs << ", " << maxArgs << ", " << pyargs << "))" << endl; + } + { + Indentation indent(INDENT); + s << INDENT << "return " << m_currentErrorCode << ';' << endl; + } + s << endl; +} + +void CppGenerator::writeCppSelfDefinition(QTextStream& s, const AbstractMetaFunction* func) +{ + if (!func->ownerClass() || func->isStatic() || func->isConstructor()) + return; + + s << INDENT; +#ifdef AVOID_PROTECTED_HACK + QString _wrapperName = wrapperName(func->ownerClass()); + bool hasProtectedMembers = func->ownerClass()->hasProtectedMembers(); + s << (hasProtectedMembers ? _wrapperName : func->ownerClass()->qualifiedCppName()) << "* " CPP_SELF_VAR " = "; + s << (hasProtectedMembers ? QString("(%1*)").arg(_wrapperName) : ""); +#else + s << func->ownerClass()->qualifiedCppName() << "* " CPP_SELF_VAR " = "; +#endif + s << cpythonWrapperCPtr(func->ownerClass(), "self") << ';' << endl; + if (func->isUserAdded()) + s << INDENT << "(void)" CPP_SELF_VAR "; // avoid warnings about unused variables" << endl; +} + +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" : "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 = "str"; + } else if (argType->isPrimitive()) { + const PrimitiveTypeEntry* ptp = reinterpret_cast(argType->typeEntry()); + while (ptp->aliasedTypeEntry()) + ptp = ptp->aliasedTypeEntry(); + + 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 (!arg->defaultValueExpression().isEmpty()) { + strArg += " = "; + if ((isCString(argType) || argType->isValuePointer() || argType->typeEntry()->isObject()) + && 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::writeInvalidCppObjectCheck(QTextStream& s, QString pyArgName, const TypeEntry* type) +{ + s << INDENT << "if (Shiboken::cppObjectIsInvalid(" << pyArgName << "))" << endl; + Indentation indent(INDENT); + s << INDENT << "return " << m_currentErrorCode << ';' << endl; +} + +void CppGenerator::writeTypeCheck(QTextStream& s, const AbstractMetaType* argType, QString argumentName, bool isNumber, QString customType) +{ + if (!customType.isEmpty()) + s << guessCPythonCheckFunction(customType); + else if (argType->isEnum()) + s << cpythonIsConvertibleFunction(argType, false); + else + s << cpythonIsConvertibleFunction(argType, isNumber); + + s << '(' << argumentName << ')'; +} + +void CppGenerator::writeTypeCheck(QTextStream& s, const OverloadData* overloadData, QString argumentName) +{ + QSet numericTypes; + + foreach (OverloadData* od, overloadData->previousOverloadData()->nextOverloadData()) { + foreach (const AbstractMetaFunction* func, od->overloads()) { + const AbstractMetaArgument* arg = od->argument(func); + + if (!arg->type()->isPrimitive()) + continue; + if (ShibokenGenerator::isNumber(arg->type()->typeEntry())) + numericTypes << arg->type()->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() : ""); + writeTypeCheck(s, argType, argumentName, numberType, customType); +} + +void CppGenerator::writeArgumentConversion(QTextStream& s, + const AbstractMetaType* argType, + QString argName, QString pyArgName, + const AbstractMetaClass* context, + QString defaultValue) +{ + const TypeEntry* type = argType->typeEntry(); + + if (type->isCustom() || type->isVarargs()) + return; + + QString typeName; + QString baseTypeName = type->name(); + bool isWrappedCppClass = type->isValue() || type->isObject(); + if (isWrappedCppClass) + typeName = baseTypeName + '*'; + else + typeName = translateTypeForWrapperMethod(argType, context); + + if (type->isContainer() || type->isPrimitive()) { + // If the type is a const char*, we don't remove the "const". + if (typeName.startsWith("const ") && !(isCString(argType))) + typeName.remove(0, sizeof("const ") / sizeof(char) - 1); + if (typeName.endsWith("&")) + typeName.chop(1); + } + typeName = typeName.trimmed(); + + bool hasImplicitConversions = !implicitConversions(argType).isEmpty(); + + if (isWrappedCppClass) { + const TypeEntry* typeEntry = (hasImplicitConversions ? type : 0); + writeInvalidCppObjectCheck(s, pyArgName, typeEntry); + } + + // Auto pointer to dealloc new objects created because to satisfy implicit conversion. + if (hasImplicitConversions) + s << INDENT << "std::auto_ptr<" << baseTypeName << " > " << argName << "_auto_ptr;" << endl; + + // Value type that has default value. + if (argType->isValue() && !defaultValue.isEmpty()) + s << INDENT << baseTypeName << ' ' << argName << "_tmp = " << defaultValue << ';' << endl; + + if (usePySideExtensions() && typeName == "QStringRef") { + s << INDENT << "QString " << argName << "_qstring = "; + if (!defaultValue.isEmpty()) + s << pyArgName << " ? "; + s << "Shiboken::Converter::toCpp(" << pyArgName << ')' << endl; + if (!defaultValue.isEmpty()) + s << " : " << defaultValue; + s << ';' << endl; + s << INDENT << "QStringRef " << argName << "(&" << argName << "_qstring);" << endl; + } else { + s << INDENT << typeName << ' ' << argName << " = "; + if (!defaultValue.isEmpty()) + s << pyArgName << " ? "; + s << "Shiboken::Converter<" << typeName << " >::toCpp(" << pyArgName << ')'; + if (!defaultValue.isEmpty()) { + s << " : "; + if (argType->isValue()) + s << '&' << argName << "_tmp"; + else + s << defaultValue; + } + s << ';' << endl; + } + + if (hasImplicitConversions) { + s << INDENT << "if ("; + if (!defaultValue.isEmpty()) + s << pyArgName << " && "; + s << '!' << cpythonCheckFunction(type) << '(' << pyArgName << "))"; + s << endl; + Indentation indent(INDENT); + s << INDENT << argName << "_auto_ptr = std::auto_ptr<" << baseTypeName; + s << " >(" << argName << ");" << endl; + } +} + +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; + 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; + + 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(); + + if (isFirst) { + isFirst = false; + s << INDENT; + } else { + s << " else "; + } + + QString typeChecks; + QTextStream tck(&typeChecks); + + QString pyArgName = (usePyArgs && maxArgs > 1) ? QString("pyargs[%1]").arg(overloadData->argPos()) : "arg"; + + OverloadData* od = overloadData; + int startArg = od->argPos(); + int sequenceArgCount = 0; + while (od && !od->argType()->isVarargs()) { + if (usePyArgs) + pyArgName = QString("pyargs[%1]").arg(od->argPos()); + + writeTypeCheck(tck, od, pyArgName); + 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 (!od->argType()->isVarargs()) + tck << " && "; + } + } + + s << "if ("; + 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; + s << "numArgs " << (lastArgIsVarargs ? ">=" : "==") << " " << numArgs << " && "; + } else if (sequenceArgCount > 1) { + s << "numArgs >= " << (startArg + sequenceArgCount) << " && "; + } + + if (refFunc->isOperatorOverload()) + s << (refFunc->isReverseOperator() ? "" : "!") << "isReverse && "; + + s << typeChecks << ") {" << 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 << "{" << endl; + { + Indentation indent(INDENT); + if (overloadData.hasAllowThread()) + s << INDENT << "Shiboken::ThreadStateSaver " THREAD_STATE_SAVER_VAR ";" << 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->minimalSignature() << endl; + s << INDENT << '{' << endl; + { + Indentation indent(INDENT); + writeSingleFunctionCall(s, overloadData, func); + s << INDENT << "break;" << endl; + } + s << INDENT << '}' << endl; + } + } + } + s << INDENT << '}' << endl; + } + s << INDENT << '}' << endl; +} + +void CppGenerator::writeSingleFunctionCall(QTextStream& s, const OverloadData& overloadData, const AbstractMetaFunction* func) +{ + 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; + } + + const AbstractMetaClass* implementingClass = overloadData.referenceFunction()->implementingClass(); + bool usePyArgs = pythonFunctionWrapperUsesListOfArguments(overloadData); + + // Handle named arguments. + writeNamedArgumentResolution(s, func, usePyArgs); + + int removedArgs = 0; + for (int i = 0; i < func->arguments().count(); i++) { + if (func->argumentRemoved(i + 1)) { + removedArgs++; + continue; + } + + if (!func->conversionRule(TypeSystem::NativeCode, i + 1).isEmpty()) + continue; + + const AbstractMetaArgument* arg = func->arguments().at(i); + + QString typeReplaced = func->typeReplaced(arg->argumentIndex() + 1); + const AbstractMetaType* argType = 0; + if (typeReplaced.isEmpty()) + argType = arg->type(); + else + argType = buildAbstractMetaTypeFromString(typeReplaced); + + if (argType) { + QString argName = QString("cpp_arg%1").arg(i - removedArgs); + QString pyArgName = usePyArgs ? QString("pyargs[%1]").arg(i - removedArgs) : "arg"; + QString defaultValue = guessScopeForDefaultValue(func, arg); + + writeArgumentConversion(s, argType, argName, pyArgName, implementingClass, defaultValue); + + // Free a custom type created by buildAbstractMetaTypeFromString. + if (argType != arg->type()) + delete argType; + } + } + + 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; +} + +void CppGenerator::writeNamedArgumentResolution(QTextStream& s, const AbstractMetaFunction* func, bool usePyArgs) +{ + AbstractMetaArgumentList args = OverloadData::getArgumentsWithDefaultValues(func); + if (!args.isEmpty()) { + s << INDENT << "if (kwds) {" << endl; + { + Indentation indent(INDENT); + s << INDENT << "const char* errorArgName = 0;" << endl; + s << INDENT << "PyObject* "; + foreach (const AbstractMetaArgument* arg, args) { + int pyArgIndex = arg->argumentIndex() - OverloadData::numberOfRemovedArguments(func, arg->argumentIndex()); + QString pyArgName = usePyArgs ? QString("pyargs[%1]").arg(pyArgIndex) : "arg"; + s << "value = PyDict_GetItemString(kwds, \"" << arg->name() << "\");" << endl; + s << INDENT << "if (value) {" << endl; + { + Indentation indent(INDENT); + s << INDENT << "if (" << pyArgName << ")" << endl; + { + Indentation indent(INDENT); + s << INDENT << "errorArgName = \"" << arg->name() << "\";" << endl; + } + s << INDENT << "else" << endl; + { + Indentation indent(INDENT); + s << INDENT << pyArgName << " = value;" << endl; + } + } + s << INDENT << '}' << endl; + s << INDENT; + } + s << "if (errorArgName) {" << endl; + { + Indentation indent(INDENT); + s << INDENT << "PyErr_Format(PyExc_TypeError, \"" << fullPythonFunctionName(func); + s << "(): got multiple values for keyword argument '%s'\", errorArgName);" << endl; + s << INDENT << "return " << m_currentErrorCode << ';' << endl; + } + s << INDENT << '}' << endl; + + } + s << INDENT << '}' << endl; + } +} + +QString CppGenerator::argumentNameFromIndex(const AbstractMetaFunction* func, int argIndex, const AbstractMetaClass** wrappedClass) +{ + *wrappedClass = 0; + QString pyArgName; + if (argIndex == -1) { + pyArgName = QString("self"); + *wrappedClass = func->implementingClass(); + } else if (argIndex == 0) { + if (func->type()) { + pyArgName = PYTHON_RETURN_VAR; + *wrappedClass = classes().findClass(func->type()->typeEntry()->name()); + } else { + ReportHandler::warning("Invalid Argument index on function modification: " + func->name()); + } + } else { + int realIndex = argIndex - 1 - OverloadData::numberOfRemovedArguments(func, argIndex - 1); + *wrappedClass = classes().findClass(func->arguments().at(realIndex)->type()->typeEntry()->name()); + if (argIndex == 1 + && OverloadData::isSingleArgument(getFunctionGroups(func->implementingClass())[func->name()])) + pyArgName = QString("arg"); + else + pyArgName = QString("pyargs[%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 (SbkBaseWrapper_containsCppWrapper(self)) {\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; + + if (func->allowThread()) + s << INDENT << THREAD_STATE_SAVER_VAR ".save();" << endl; + + 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; + } + + CodeSnipList convRules = getConversionRule(TypeSystem::NativeCode, func); + if (convRules.size()) + writeCodeSnips(s, convRules, CodeSnip::Beginning, TypeSystem::TargetLangCode, func); + + // Code to restore the threadSaver has been written? + bool threadRestored = false; + + if (!func->isUserAdded()) { + bool badModifications = false; + QStringList userArgs; + + if (!func->isCopyConstructor()) { + int removedArgs = 0; + for (int i = 0; i < maxArgs + removedArgs; i++) { + const AbstractMetaArgument* arg = func->arguments().at(i); + 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 + bool hasConversionRule = !func->conversionRule(TypeSystem::NativeCode, arg->argumentIndex() + 1).isEmpty(); + if (hasConversionRule) { + userArgs << arg->name() + "_out"; + } else { + if (arg->defaultValueExpression().isEmpty()) + badModifications = true; + else + userArgs << guessScopeForDefaultValue(func, arg); + } + } else { + int idx = arg->argumentIndex() - removedArgs; + QString argName; + + bool hasConversionRule = !func->conversionRule(TypeSystem::NativeCode, arg->argumentIndex() + 1).isEmpty(); + if (hasConversionRule) { + argName = arg->name() + "_out"; + } else { + argName = QString("cpp_arg%1").arg(idx); + if (shouldDereferenceArgumentPointer(arg)) + argName.prepend('*'); + } + 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 (!arg->defaultValueExpression().isEmpty()) + otherArgs.prepend(guessScopeForDefaultValue(func, arg)); + else if (hasConversionRule) + otherArgs.prepend(arg->name() + "_out"); + else + badModifications = true; + } + if (otherArgsModified) + userArgs << otherArgs; + } + + bool isCtor = false; + QString methodCall; + QTextStream mc(&methodCall); + + if (badModifications) { + // When an argument is removed from a method signature and no other + // means of calling the method is provided (as with code injection) + // the generator must write a compiler error line stating the situation. + if (func->injectedCodeSnips(CodeSnip::Any, TypeSystem::TargetLangCode).isEmpty()) { + qFatal(qPrintable("No way to call \"" + func->ownerClass()->name() + + "::" + func->minimalSignature() + +"\" with the modifications described in the type system file")); + } + } else if (func->isOperatorOverload()) { + QString firstArg("(*" CPP_SELF_VAR ")"); + 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); + mc << firstArg << ' ' << op << ' ' << secondArg; + } else { + mc << op << ' ' << secondArg; + } + } else if (!injectedCodeCallsCppFunction(func)) { + if (func->isConstructor() || func->isCopyConstructor()) { + isCtor = true; + QString className = wrapperName(func->ownerClass()); + mc << "new " << className << '('; + if (func->isCopyConstructor() && maxArgs == 1) { + mc << '*'; + QString arg("cpp_arg0"); + if (shouldGenerateCppWrapper(func->ownerClass())) + arg = QString("reinterpret_cast<%1*>(%2)").arg(className).arg(arg); + mc << arg; + } else { + mc << userArgs.join(", "); + } + mc << ')'; + } else { + if (func->ownerClass()) { +#ifndef AVOID_PROTECTED_HACK + if (!func->isStatic()) + mc << CPP_SELF_VAR "->"; + if (!func->isAbstract()) + mc << func->ownerClass()->qualifiedCppName() << "::"; + mc << func->originalName(); +#else + if (!func->isStatic()) { + if (func->isProtected()) + mc << "((" << wrapperName(func->ownerClass()) << "*) "; + mc << CPP_SELF_VAR << (func->isProtected() ? ")" : "") << "->"; + } + if (!func->isAbstract()) + mc << (func->isProtected() ? wrapperName(func->ownerClass()) : func->ownerClass()->qualifiedCppName()) << "::"; + mc << func->originalName() << (func->isProtected() ? "_protected" : ""); +#endif + } else { + mc << func->originalName(); + } + mc << '(' << userArgs.join(", ") << ')'; + } + } + + if (!injectedCodeCallsCppFunction(func)) { + s << INDENT; + if (isCtor) { + s << "cptr = "; + } else if (func->type() && !func->isInplaceOperator()) { +#ifdef AVOID_PROTECTED_HACK + QString enumName; + const AbstractMetaEnum* metaEnum = findAbstractMetaEnum(func->type()); + if (metaEnum) { + if (metaEnum->isProtected()) + enumName = protectedEnumSurrogateName(metaEnum); + else + enumName = func->type()->cppSignature(); + methodCall.prepend(enumName + '('); + methodCall.append(')'); + s << enumName; + } else +#endif + s << func->type()->cppSignature(); + s << " " CPP_RETURN_VAR " = "; + } + s << methodCall << ';' << endl; + + if (func->allowThread()) { + s << INDENT << THREAD_STATE_SAVER_VAR ".restore();" << endl; + threadRestored = true; + } + + if (!isCtor && !func->isInplaceOperator() && func->type()) { + s << INDENT << PYTHON_RETURN_VAR " = "; + writeToPythonConversion(s, func->type(), func->ownerClass(), CPP_RETURN_VAR); + s << ';' << endl; + } + } + } + + if (!threadRestored && func->allowThread()) + s << INDENT << THREAD_STATE_SAVER_VAR ".restore();" << 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 (!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) + 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; + if (arg_mod.ownerships[TypeSystem::TargetLangCode] == TypeSystem::TargetLangOwnership) { + s << "SbkBaseWrapper_setOwnership(" << pyArgName << ", true);"; + } else if (wrappedClass->hasVirtualDestructor()) { + if (arg_mod.index == 0) { + s << "SbkBaseWrapper_setOwnership(" PYTHON_RETURN_VAR ", 0);"; + } else { + s << "BindingManager::instance().transferOwnershipToCpp(" << pyArgName << ");"; + } + } else { + s << "BindingManager::instance().invalidateWrapper(" << pyArgName << ");"; + } + s << endl; + } + + } else if (!refcount_mods.isEmpty()) { + foreach (ArgumentModification arg_mod, refcount_mods) { + if (arg_mod.referenceCounts.first().action != ReferenceCount::Add) + continue; + const AbstractMetaClass* wrappedClass = 0; + QString pyArgName = argumentNameFromIndex(func, arg_mod.index, &wrappedClass); + if (pyArgName.isEmpty()) { + s << "#error Invalid reference count modification for argument " << arg_mod.index << endl << endl; + break; + } + + s << INDENT << "Shiboken::keepReference(reinterpret_cast(self), \""; + QString varName = arg_mod.referenceCounts.first().varName; + if (varName.isEmpty()) + varName = func->minimalSignature() + QString().number(arg_mod.index); + + s << varName << "\", " << pyArgName << ");" << 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, SbkBaseWrapperType* 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::writeExtendedIsConvertibleFunction(QTextStream& s, const TypeEntry* externalType, const QList& conversions) +{ + s << "static bool " << extendedIsConvertibleFunctionName(externalType) << "(PyObject* pyobj)" << endl; + s << '{' << endl; + s << INDENT << "return "; + bool isFirst = true; + foreach (const AbstractMetaClass* metaClass, conversions) { + Indentation indent(INDENT); + if (isFirst) + isFirst = false; + else + s << endl << INDENT << " || "; + s << cpythonIsConvertibleFunction(metaClass->typeEntry()) << "(pyobj)"; + } + s << ';' << endl; + s << '}' << endl; +} + +void CppGenerator::writeExtendedToCppFunction(QTextStream& s, const TypeEntry* externalType, const QList& conversions) +{ + s << "static void* " << extendedToCppFunctionName(externalType) << "(PyObject* pyobj)" << endl; + s << '{' << endl; + s << INDENT << "void* cptr = 0;" << endl; + bool isFirst = true; + foreach (const AbstractMetaClass* metaClass, conversions) { + s << INDENT; + if (isFirst) + isFirst = false; + else + s << "else "; + s << "if (" << cpythonIsConvertibleFunction(metaClass->typeEntry()) << "(pyobj))" << endl; + Indentation indent(INDENT); + s << INDENT << "cptr = new " << externalType->name() << '('; + writeToCppConversion(s, metaClass, "pyobj"); + s << ");" << endl; + } + s << INDENT << "return cptr;" << endl; + s << '}' << endl; +} + +void CppGenerator::writeExtendedConverterInitialization(QTextStream& s, const TypeEntry* externalType, const QList& conversions) +{ + s << INDENT << "// Extended implicit conversions for " << externalType->targetLangPackage() << '.' << externalType->name() << endl; + s << INDENT << "shiboType = reinterpret_cast("; + s << cppApiVariableName(externalType->targetLangPackage()) << '['; + s << getTypeIndexVariableName(externalType) << "]);" << endl; + s << INDENT << "shiboType->ext_isconvertible = " << extendedIsConvertibleFunctionName(externalType) << ';' << endl; + s << INDENT << "shiboType->ext_tocpp = " << extendedToCppFunctionName(externalType) << ';' << endl; +} + +QString CppGenerator::multipleInheritanceInitializerFunctionName(const AbstractMetaClass* metaClass) +{ + if (!hasMultipleInheritanceInAncestry(metaClass)) + return QString(); + return QString("%1_mi_init").arg(cpythonBaseName(metaClass->typeEntry())); +} + +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 && (reinterpret_cast(baseType)->type() == ContainerTypeEntry::ListContainer)) + 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 cpp_dtor('0'); + QString tp_as_number('0'); + QString tp_as_sequence('0'); + QString tp_hash('0'); + QString mi_init('0'); + QString obj_copier('0'); + QString mi_specialcast('0'); + QString cppClassName = metaClass->qualifiedCppName(); + QString className = cpythonTypeName(metaClass).replace(QRegExp("_Type$"), ""); + QString baseClassName('0'); + AbstractMetaFunctionList ctors = metaClass->queryFunctions(AbstractMetaClass::Constructors); + bool onlyPrivCtor = !metaClass->hasNonPrivateConstructor(); + + if (metaClass->hasArithmeticOperatorOverload() + || metaClass->hasLogicalOperatorOverload() + || metaClass->hasBitwiseOperatorOverload()) { + tp_as_number = QString("&%1_as_number").arg(cpythonBaseName(metaClass)); + } + + // sequence protocol check + if (supportsSequenceProtocol(metaClass)) + tp_as_sequence = QString("&Py%1_as_sequence").arg(cppClassName); + + if (!metaClass->baseClass()) + baseClassName = "reinterpret_cast(&Shiboken::SbkBaseWrapper_Type)"; + + if (metaClass->isNamespace() || metaClass->hasPrivateDestructor()) { + tp_flags = "Py_TPFLAGS_DEFAULT|Py_TPFLAGS_CHECKTYPES"; + tp_dealloc = metaClass->hasPrivateDestructor() ? + "Shiboken::deallocWrapperWithPrivateDtor" : "0"; + tp_init = "0"; + } else { + if (onlyPrivCtor) + tp_flags = "Py_TPFLAGS_DEFAULT|Py_TPFLAGS_CHECKTYPES"; + else + tp_flags = "Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_CHECKTYPES"; + + QString deallocClassName; + if (shouldGenerateCppWrapper(metaClass)) + deallocClassName = wrapperName(metaClass); + else + deallocClassName = cppClassName; + tp_dealloc = "&Shiboken::deallocWrapper"; + + QString dtorClassName = metaClass->qualifiedCppName(); +#ifdef AVOID_PROTECTED_HACK + if (metaClass->hasProtectedDestructor()) + dtorClassName = wrapperName(metaClass); +#endif + cpp_dtor = "&Shiboken::callCppDestructor<" + dtorClassName + " >"; + + tp_init = onlyPrivCtor ? "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 = "Shiboken::SbkBaseWrapper_TpNew"; + + 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); + } + + // class or some ancestor has multiple inheritance + const AbstractMetaClass* miClass = getMultipleInheritingClass(metaClass); + if (miClass) { + if (metaClass == miClass) { + mi_init = multipleInheritanceInitializerFunctionName(miClass); + writeMultipleInheritanceInitializerFunction(s, metaClass); + } + mi_specialcast = '&'+cpythonSpecialCastFunctionName(metaClass); + writeSpecialCastFunction(s, metaClass); + s << endl; + } + + if (metaClass->typeEntry()->isValue() && shouldGenerateCppWrapper(metaClass)) + obj_copier = '&' + cpythonBaseName(metaClass) + "_ObjCopierFunc"; + + if (!metaClass->typeEntry()->hashFunction().isEmpty()) + tp_hash = '&' + cpythonBaseName(metaClass) + "_HashFunc"; + + s << "// Class Definition -----------------------------------------------" << endl; + s << "extern \"C\" {" << endl; + s << "static SbkBaseWrapperType " << className + "_Type" << " = { { {" << endl; + s << INDENT << "PyObject_HEAD_INIT(&Shiboken::SbkBaseWrapperType_Type)" << endl; + s << INDENT << "/*ob_size*/ 0," << endl; + s << INDENT << "/*tp_name*/ \"" << metaClass->fullName() << "\"," << endl; + s << INDENT << "/*tp_basicsize*/ sizeof(Shiboken::SbkBaseWrapper)," << 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*/ " << tp_as_number << ',' << endl; + s << INDENT << "/*tp_as_sequence*/ " << tp_as_sequence << ',' << endl; + s << INDENT << "/*tp_as_mapping*/ 0," << endl; + s << INDENT << "/*tp_hash*/ " << tp_hash << ',' << endl; + s << INDENT << "/*tp_call*/ 0," << 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*/ 0," << endl; + s << INDENT << "/*tp_clear*/ 0," << endl; + s << INDENT << "/*tp_richcompare*/ " << tp_richcompare << ',' << endl; + s << INDENT << "/*tp_weaklistoffset*/ 0," << endl; + s << INDENT << "/*tp_iter*/ 0," << endl; + s << INDENT << "/*tp_iternext*/ 0," << 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 << "/*mi_offsets*/ 0," << endl; + s << INDENT << "/*mi_init*/ " << mi_init << ',' << endl; + s << INDENT << "/*mi_specialcast*/ " << mi_specialcast << ',' << endl; + s << INDENT << "/*type_discovery*/ 0," << endl; + s << INDENT << "/*obj_copier*/ " << obj_copier << ',' << endl; + s << INDENT << "/*ext_isconvertible*/ 0," << endl; + s << INDENT << "/*ext_tocpp*/ 0," << endl; + s << INDENT << "/*cpp_dtor*/ " << cpp_dtor << ',' << endl; + s << INDENT << "/*is_multicpp*/ 0," << endl; + s << INDENT << "/*is_user_type*/ 0," << endl; + QString suffix; + if (metaClass->typeEntry()->isObject() || metaClass->typeEntry()->isQObject()) + suffix = "*"; + s << INDENT << "/*original_name*/ \"" << metaClass->qualifiedCppName() << suffix << "\"," << endl; + s << INDENT << "/*user_data*/ 0" << endl; + s << "};" << endl; + s << "} //extern" << 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; + writeInvalidCppObjectCheck(s); + + 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) +{ + QString className = metaClass->qualifiedCppName(); + QMap funcs; + + bool hasFunctions = false; + foreach(QString funcName, m_sequenceProtocol.keys()) { + const AbstractMetaFunction* func = metaClass->findFunction(funcName); + funcs[funcName] = func ? cpythonFunctionName(func).prepend("&") : "0"; + if (!hasFunctions && func) + hasFunctions = true; + } + + //use default implementation + if (!hasFunctions) { + QString baseName = cpythonBaseName(metaClass->typeEntry()); + funcs["__len__"] = baseName + "__len__"; + funcs["__getitem__"] = baseName + "__getitem__"; + funcs["__setitem__"] = baseName + "__setitem__"; + } + + s << "static PySequenceMethods Py" << className << "_as_sequence = {\n" + << INDENT << "/*sq_length*/ " << funcs["__len__"] << ",\n" + << INDENT << "/*sq_concat*/ " << funcs["__concat__"] << ",\n" + << INDENT << "/*sq_repeat*/ 0,\n" + << INDENT << "/*sq_item*/ " << funcs["__getitem__"] << ",\n" + << INDENT << "/*sq_slice*/ " << funcs["__getslice__"] << ",\n" + << INDENT << "/*sq_ass_item*/ " << funcs["__setitem__"] << ",\n" + << INDENT << "/*sq_ass_slice*/ " << funcs["__setslice__"] << ",\n" + << INDENT << "/*sq_contains*/ " << funcs["__contains__"] << ",\n" + << INDENT << "/*sq_inplace_concat*/ 0,\n" + << INDENT << "/*sq_inplace_repeat*/ 0\n" + << "};\n\n"; +} + +void CppGenerator::writeTypeAsNumberDefinition(QTextStream& s, const AbstractMetaClass* metaClass) +{ + QMap nb; + + nb["__add__"] = QString('0'); + nb["__sub__"] = QString('0'); + nb["__mul__"] = QString('0'); + nb["__div__"] = QString('0'); + nb["__mod__"] = QString('0'); + nb["__neg__"] = QString('0'); + nb["__pos__"] = QString('0'); + nb["__invert__"] = QString('0'); + nb["__lshift__"] = QString('0'); + nb["__rshift__"] = QString('0'); + nb["__and__"] = QString('0'); + nb["__xor__"] = QString('0'); + nb["__or__"] = QString('0'); + nb["__iadd__"] = QString('0'); + nb["__isub__"] = QString('0'); + nb["__imul__"] = QString('0'); + nb["__idiv__"] = QString('0'); + nb["__imod__"] = QString('0'); + nb["__ilshift__"] = QString('0'); + nb["__irshift__"] = QString('0'); + nb["__iand__"] = QString('0'); + nb["__ixor__"] = QString('0'); + nb["__ior__"] = QString('0'); + + 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); + } + + s << "static PyNumberMethods " << cpythonBaseName(metaClass); + s << "_as_number = {" << endl; + s << INDENT << "/*nb_add*/ (binaryfunc)" << nb["__add__"] << ',' << endl; + s << INDENT << "/*nb_subtract*/ (binaryfunc)" << nb["__sub__"] << ',' << endl; + s << INDENT << "/*nb_multiply*/ (binaryfunc)" << nb["__mul__"] << ',' << endl; + s << INDENT << "/*nb_divide*/ (binaryfunc)" << nb["__div__"] << ',' << endl; + s << INDENT << "/*nb_remainder*/ (binaryfunc)" << nb["__mod__"] << ',' << endl; + s << INDENT << "/*nb_divmod*/ 0," << endl; + s << INDENT << "/*nb_power*/ 0," << endl; + s << INDENT << "/*nb_negative*/ (unaryfunc)" << nb["__neg__"] << ',' << endl; + s << INDENT << "/*nb_positive*/ (unaryfunc)" << nb["__pos__"] << ',' << endl; + s << INDENT << "/*nb_absolute*/ 0," << endl; + s << INDENT << "/*nb_nonzero*/ 0," << endl; + s << INDENT << "/*nb_invert*/ (unaryfunc)" << nb["__invert__"] << ',' << endl; + s << INDENT << "/*nb_lshift*/ (binaryfunc)" << nb["__lshift__"] << ',' << endl; + s << INDENT << "/*nb_rshift*/ (binaryfunc)" << nb["__rshift__"] << ',' << endl; + s << INDENT << "/*nb_and*/ (binaryfunc)" << nb["__and__"] << ',' << endl; + s << INDENT << "/*nb_xor*/ (binaryfunc)" << nb["__xor__"] << ',' << endl; + s << INDENT << "/*nb_or*/ (binaryfunc)" << nb["__or__"] << ',' << endl; + s << INDENT << "/*nb_coerce*/ 0," << endl; + s << INDENT << "/*nb_int*/ 0," << endl; + s << INDENT << "/*nb_long*/ 0," << endl; + s << INDENT << "/*nb_float*/ 0," << endl; + s << INDENT << "/*nb_oct*/ 0," << endl; + s << INDENT << "/*nb_hex*/ 0," << endl; + s << INDENT << "/*nb_inplace_add*/ (binaryfunc)" << nb["__iadd__"] << ',' << endl; + s << INDENT << "/*nb_inplace_subtract*/ (binaryfunc)" << nb["__isub__"] << ',' << endl; + s << INDENT << "/*nb_inplace_multiply*/ (binaryfunc)" << nb["__imul__"] << ',' << endl; + s << INDENT << "/*nb_inplace_divide*/ (binaryfunc)" << nb["__idiv__"] << ',' << endl; + s << INDENT << "/*nb_inplace_remainder*/ (binaryfunc)" << nb["__imod__"] << ',' << endl; + s << INDENT << "/*nb_inplace_power*/ 0," << endl; + s << INDENT << "/*nb_inplace_lshift*/ (binaryfunc)" << nb["__ilshift__"] << ',' << endl; + s << INDENT << "/*nb_inplace_rshift*/ (binaryfunc)" << nb["__irshift__"] << ',' << endl; + s << INDENT << "/*nb_inplace_and*/ (binaryfunc)" << nb["__iand__"] << ',' << endl; + s << INDENT << "/*nb_inplace_xor*/ (binaryfunc)" << nb["__ixor__"] << ',' << endl; + s << INDENT << "/*nb_inplace_or*/ (binaryfunc)" << nb["__ior__"] << ',' << 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::writeGetterFunction(QTextStream& s, const AbstractMetaField* metaField) +{ + s << "static PyObject* " << cpythonGetterFunctionName(metaField) << "(PyObject* self, void*)" << endl; + s << '{' << endl; + s << INDENT << "return "; + + QString cppField; +#ifdef AVOID_PROTECTED_HACK + if (metaField->isProtected()) + cppField = QString("((%1*)%2)->%3()").arg(wrapperName(metaField->enclosingClass())).arg(cpythonWrapperCPtr(metaField->enclosingClass(), "self")).arg(protectedFieldGetterName(metaField)); + else +#endif + cppField= QString("%1->%2").arg(cpythonWrapperCPtr(metaField->enclosingClass(), "self")).arg(metaField->name()); + writeToPythonConversion(s, metaField->type(), metaField->enclosingClass(), cppField); + s << ';' << endl; + s << '}' << endl; +} + +void CppGenerator::writeSetterFunction(QTextStream& s, const AbstractMetaField* metaField) +{ + s << "static int " << cpythonSetterFunctionName(metaField) << "(PyObject* self, PyObject* value, void*)" << endl; + s << '{' << endl; + + s << INDENT << "if (value == 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; + + s << INDENT << "if (!"; + writeTypeCheck(s, metaField->type(), "value", isNumber(metaField->type()->typeEntry())); + s << ") {" << endl; + { + Indentation indent(INDENT); + s << INDENT << "PyErr_SetString(PyExc_TypeError, \"wrong type attributed to '"; + s << metaField->name() << "', '" << metaField->type()->name() << "' or convertible type expected\");" << endl; + s << INDENT << "return -1;" << endl; + } + s << INDENT << '}' << endl << endl; + + s << INDENT; +#ifdef AVOID_PROTECTED_HACK + if (metaField->isProtected()) { + QString fieldStr = QString("((%1*)%2)->%3").arg(wrapperName(metaField->enclosingClass())).arg(cpythonWrapperCPtr(metaField->enclosingClass(), "self")).arg(protectedFieldSetterName(metaField)); + s << fieldStr << '('; + writeToCppConversion(s, metaField->type(), metaField->enclosingClass(), "value"); + s << ')'; + } else { +#endif + QString fieldStr = QString("%1->%2").arg(cpythonWrapperCPtr(metaField->enclosingClass(), "self")).arg(metaField->name()); + s << fieldStr << " = "; + writeToCppConversion(s, metaField->type(), metaField->enclosingClass(), "value"); +#ifdef AVOID_PROTECTED_HACK + } +#endif + s << ';' << endl << endl; + + + bool pythonWrapperRefCounting = metaField->type()->typeEntry()->isObject() + || metaField->type()->isValuePointer(); + if (pythonWrapperRefCounting) { + s << INDENT << "Shiboken::keepReference(reinterpret_cast(self), \""; + s << metaField->name() << "\", value);" << endl; + //s << INDENT << "Py_XDECREF(oldvalue);" << endl; + s << 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* self, PyObject* other, int op)" << endl; + s << '{' << endl; + QList cmpOverloads = filterGroupedOperatorFunctions(metaClass, AbstractMetaClass::ComparisonOp); + s << INDENT << "bool result = false;" << endl; + s << INDENT << metaClass->qualifiedCppName() << "& cpp_self = *" << cpythonWrapperCPtr(metaClass) << ';' << endl; + s << endl; + + s << INDENT << "switch (op) {" << endl; + { + Indentation indent(INDENT); + foreach (AbstractMetaFunctionList overloads, cmpOverloads) { + OverloadData overloadData(overloads, this); + const AbstractMetaFunction* rfunc = overloads[0]; + + s << INDENT << "case " << ShibokenGenerator::pythonRichCompareOperatorId(rfunc) << ':' << 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; + bool comparesWithSameType = false; + foreach (const AbstractMetaFunction* func, overloads) { + if (func->isStatic()) + continue; + + const AbstractMetaType* type = func->arguments()[0]->type(); + bool numberType = alternativeNumericTypes == 1 || ShibokenGenerator::isPyInt(type); + + if (!comparesWithSameType) + comparesWithSameType = type->typeEntry() == metaClass->typeEntry(); + + if (!first) { + s << " else "; + } else { + first = false; + s << INDENT; + } + + s << "if (" << cpythonIsConvertibleFunction(type, numberType) << "(other)) {" << endl; + { + Indentation indent(INDENT); + s << INDENT << "// " << func->signature() << endl; + s << INDENT; + AbstractMetaClass* clz = classes().findClass(type->typeEntry()); + if (type->typeEntry()->isValue()) { + Q_ASSERT(clz); + s << clz->qualifiedCppName() << '*'; + } else + s << translateTypeForWrapperMethod(type, metaClass); + s << " cpp_other = "; + if (type->typeEntry()->isValue()) + s << cpythonWrapperCPtr(type, "other"); + else + writeToCppConversion(s, type, metaClass, "other"); + s << ';' << endl; + + s << INDENT << "result = "; + // It's a value type and the conversion for a pointer returned null. + if (type->typeEntry()->isValue()) { + s << "!cpp_other ? cpp_self == "; + writeToCppConversion(s, type, metaClass, "other", ExcludeReference | ExcludeConst); + s << " : "; + } + s << "(cpp_self " << op << ' ' << (type->typeEntry()->isValue() ? "(*" : ""); + s << "cpp_other" << (type->typeEntry()->isValue() ? ")" : "") << ");" << endl; + } + s << INDENT << '}'; + } + + // Compares with implicit conversions + if (comparesWithSameType && !metaClass->implicitConversions().isEmpty()) { + AbstractMetaType temporaryType; + temporaryType.setTypeEntry(metaClass->typeEntry()); + temporaryType.setConstant(true); + temporaryType.setReference(false); + temporaryType.setTypeUsagePattern(AbstractMetaType::ValuePattern); + s << " else if (" << cpythonIsConvertibleFunction(metaClass->typeEntry()); + s << "(other)) {" << endl; + { + Indentation indent(INDENT); + writeArgumentConversion(s, &temporaryType, "cpp_other", "other", metaClass); + s << INDENT << "result = (cpp_self " << op << " (*cpp_other));" << endl; + } + s << INDENT << '}'; + } + + s << " else goto " << baseName << "_RichComparison_TypeError;" << endl; + s << endl; + + s << INDENT << "break;" << endl; + } + s << INDENT << "default:" << endl; + { + Indentation indent(INDENT); + s << INDENT << "PyErr_SetString(PyExc_NotImplementedError, \"operator not implemented.\");" << endl; + s << INDENT << "return " << m_currentErrorCode << ';' << endl; + } + } + s << INDENT << '}' << endl << endl; + + s << INDENT << "if (result)" << endl; + { + Indentation indent(INDENT); + s << INDENT << "Py_RETURN_TRUE;" << endl; + } + s << INDENT << baseName << "_RichComparison_TypeError:" << endl; + s << INDENT << "Py_RETURN_FALSE;" << 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::writeEnumInitialization(QTextStream& s, const AbstractMetaEnum* cppEnum) +{ + QString cpythonName = cpythonEnumName(cppEnum); + QString addFunction; + if (cppEnum->enclosingClass()) + addFunction = "PyDict_SetItemString(" + cpythonTypeName(cppEnum->enclosingClass()) + ".super.ht_type.tp_dict,"; + else if (cppEnum->isAnonymous()) + addFunction = "PyModule_AddIntConstant(module,"; + else + addFunction = "PyModule_AddObject(module,"; + + s << INDENT << "// init "; + if (cppEnum->isAnonymous()) + s << "anonymous enum identified by enum value: "; + else + s << "enum: "; + s << cppEnum->name() << endl; + + if (!cppEnum->isAnonymous()) { + s << INDENT << cpythonTypeNameExt(cppEnum->typeEntry()) << " = &" << cpythonTypeName(cppEnum->typeEntry()) << ';' << endl; + s << INDENT << "if (PyType_Ready((PyTypeObject*)&" << cpythonName << "_Type) < 0)" << endl; + s << INDENT << INDENT << "return;" << endl; + + s << INDENT << "Py_INCREF(&" << cpythonName << "_Type);" << endl; + + s << INDENT << addFunction << endl; + s << INDENT << INDENT << INDENT << '\"' << cppEnum->name() << "\","; + s << "((PyObject*)&" << cpythonName << "_Type));" << endl << endl; + + FlagsTypeEntry* flags = cppEnum->typeEntry()->flags(); + if (flags) { + QString flagsName = cpythonFlagsName(flags); + s << INDENT << "// init flags class: " << flags->name() << endl; + s << INDENT << cpythonTypeNameExt(flags) << " = &" << cpythonTypeName(flags) << ';' << endl; + + s << INDENT << "if (PyType_Ready((PyTypeObject*)&" << flagsName << "_Type) < 0)" << endl; + s << INDENT << INDENT << "return;" << endl; + + s << INDENT << "Py_INCREF(&" << flagsName << "_Type);" << endl; + + s << INDENT << addFunction << endl; + s << INDENT << INDENT << INDENT << '\"' << flags->flagsName() << "\","; + s << "((PyObject*)&" << flagsName << "_Type));" << endl << endl; + } + } + + + foreach (const AbstractMetaEnumValue* enumValue, cppEnum->values()) { + if (cppEnum->typeEntry()->isEnumValueRejected(enumValue->name())) + continue; + + QString enumValueText; +#ifdef AVOID_PROTECTED_HACK + if (!cppEnum->isProtected()) { +#endif + enumValueText = "(long) "; + if (cppEnum->enclosingClass()) + enumValueText += cppEnum->enclosingClass()->qualifiedCppName() + "::"; + enumValueText += enumValue->name(); +#ifdef AVOID_PROTECTED_HACK + } else { + enumValueText += enumValue->value(); + } +#endif + + bool shouldDecrefNumber = false; + QString enumItemText = "enum_item"; + if (!cppEnum->isAnonymous()) { + s << INDENT << "enum_item = Shiboken::SbkEnumObject_New(&"; + s << cpythonName << "_Type," << endl; + { + Indentation indent(INDENT); + s << INDENT << enumValueText << ", \"" << enumValue->name() << "\");" << endl; + } + } else if (cppEnum->enclosingClass()) { + s << INDENT << "enum_item = PyInt_FromLong(" << enumValueText << ");" << endl; + shouldDecrefNumber = true; + } else { + enumItemText = enumValueText; + } + + s << INDENT << addFunction << endl; + { + Indentation indent(INDENT); + s << INDENT << '"' << enumValue->name() << "\", " << enumItemText << ");" << endl; + } + if (shouldDecrefNumber) + s << INDENT << "Py_DECREF(enum_item);" << endl; + + if (!cppEnum->isAnonymous()) { + s << INDENT << "PyDict_SetItemString(" << cpythonName << "_Type.tp_dict," << endl; + { + Indentation indent(INDENT); + s << INDENT << '"' << enumValue->name() << "\", enum_item);" << endl; + } + } + + } + + if (!cppEnum->isAnonymous()) { + // TypeResolver stuff + s << INDENT << "Shiboken::TypeResolver::createValueTypeResolver(\""; + if (cppEnum->enclosingClass()) + s << cppEnum->enclosingClass()->qualifiedCppName() << "::"; + s << cppEnum->name() << "\");\n"; + } + + + s << endl; +} + +void CppGenerator::writeSignalInitialization(QTextStream& s, const AbstractMetaClass* metaClass) +{ + QHash signatures; + + foreach (const AbstractMetaFunction* cppSignal, metaClass->cppSignalFunctions()) { + QString signature; + if (cppSignal->declaringClass() == metaClass) { + if (cppSignal->arguments().count()) { + for (int i = 0; i < cppSignal->arguments().count(); ++i) { + if (i > 0) + signature += ", "; + AbstractMetaArgument *a = cppSignal->arguments().at(i); + signature += a->type()->cppSignature(); + } + } else { + signature = "void"; + } + signatures[cppSignal->name()].append(QMetaObject::normalizedSignature(signature.toAscii())); + } + } + + if (signatures.size() == 0) + return; + + s << INDENT << "// Initialize signals" << endl; + s << INDENT << "PyObject* signal_item;" << endl << endl; + + foreach(QString funcName, signatures.keys()) { + s << INDENT << "signal_item = PySide::signalNew(\"" << funcName <<"\""; + foreach(QString signature, signatures[funcName]) + s << ", \"" + signature << "\""; + s << ", NULL);" << endl; + s << INDENT << "PyDict_SetItemString(" + cpythonTypeName(metaClass) + ".super.ht_type.tp_dict"; + s << ", \"" << funcName << "\", signal_item);" << endl; + s << INDENT << "Py_DECREF(signal_item);" << endl; + } + s << endl; +} + +void CppGenerator::writeEnumNewMethod(QTextStream& s, const AbstractMetaEnum* cppEnum) +{ + QString cpythonName = cpythonEnumName(cppEnum); + s << "static PyObject* "; + s << cpythonName << "_New(PyTypeObject* type, PyObject* args, PyObject* kwds)" << endl; + s << '{' << endl; + s << INDENT << "int item_value = 0;" << endl; + s << INDENT << "if (!PyArg_ParseTuple(args, \"|i:__new__\", &item_value))" << endl; + { + Indentation indent(INDENT); + s << INDENT << "return 0;" << endl; + } + s << INDENT << "PyObject* self = Shiboken::SbkEnumObject_New(type, item_value);" << endl << endl; + s << INDENT << "if (!self)" << endl; + { + Indentation indent(INDENT); + s << INDENT << "return 0;" << endl; + } + s << INDENT << "return self;" << endl << '}' << endl; +} + +void CppGenerator::writeEnumDefinition(QTextStream& s, const AbstractMetaEnum* cppEnum) +{ + QString cpythonName = cpythonEnumName(cppEnum); + QString tp_as_number("0"); + if (cppEnum->typeEntry()->flags()) + tp_as_number = QString("&%1_as_number").arg(cpythonName); + + + s << "static PyGetSetDef " << cpythonName << "_getsetlist[] = {" << endl; + s << INDENT << "{const_cast(\"name\"), (getter)Shiboken::SbkEnumObject_name}," << endl; + s << INDENT << "{0} // Sentinel" << endl; + s << "};" << endl << endl; + + QString newFunc = cpythonName + "_New"; + + s << "// forward declaration of new function" << endl; + s << "static PyObject* " << newFunc << "(PyTypeObject*, PyObject*, PyObject*);" << endl << endl; + + s << "static PyTypeObject " << cpythonName << "_Type = {" << endl; + s << INDENT << "PyObject_HEAD_INIT(&Shiboken::SbkEnumType_Type)" << endl; + s << INDENT << "/*ob_size*/ 0," << endl; + s << INDENT << "/*tp_name*/ \"" << cppEnum->name() << "\"," << endl; + s << INDENT << "/*tp_basicsize*/ sizeof(Shiboken::SbkEnumObject)," << endl; + s << INDENT << "/*tp_itemsize*/ 0," << endl; + s << INDENT << "/*tp_dealloc*/ 0," << 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*/ Shiboken::SbkEnumObject_repr," << endl; + s << INDENT << "/*tp_as_number*/ " << tp_as_number << ',' << endl; + s << INDENT << "/*tp_as_sequence*/ 0," << endl; + s << INDENT << "/*tp_as_mapping*/ 0," << endl; + s << INDENT << "/*tp_hash*/ 0," << endl; + s << INDENT << "/*tp_call*/ 0," << endl; + s << INDENT << "/*tp_str*/ Shiboken::SbkEnumObject_repr," << endl; + s << INDENT << "/*tp_getattro*/ 0," << endl; + s << INDENT << "/*tp_setattro*/ 0," << endl; + s << INDENT << "/*tp_as_buffer*/ 0," << endl; + s << INDENT << "/*tp_flags*/ Py_TPFLAGS_DEFAULT," << endl; + s << INDENT << "/*tp_doc*/ 0," << endl; + s << INDENT << "/*tp_traverse*/ 0," << endl; + s << INDENT << "/*tp_clear*/ 0," << endl; + s << INDENT << "/*tp_richcompare*/ 0," << endl; + s << INDENT << "/*tp_weaklistoffset*/ 0," << endl; + s << INDENT << "/*tp_iter*/ 0," << endl; + s << INDENT << "/*tp_iternext*/ 0," << endl; + s << INDENT << "/*tp_methods*/ 0," << endl; + s << INDENT << "/*tp_members*/ 0," << endl; + s << INDENT << "/*tp_getset*/ " << cpythonName << "_getsetlist," << endl; + s << INDENT << "/*tp_base*/ &PyInt_Type," << 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*/ 0," << endl; + s << INDENT << "/*tp_alloc*/ 0," << endl; + s << INDENT << "/*tp_new*/ " << newFunc << ',' << 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 << endl; + + writeEnumNewMethod(s, cppEnum); + 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", "~"); + 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 << "/*nb_divide*/ 0," << 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*/ 0," << 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 << "/*nb_coerce*/ 0," << endl; + s << INDENT << "/*nb_int*/ 0," << endl; + s << INDENT << "/*nb_long*/ 0," << endl; + s << INDENT << "/*nb_float*/ 0," << endl; + s << INDENT << "/*nb_oct*/ 0," << endl; + s << INDENT << "/*nb_hex*/ 0," << endl; + s << INDENT << "/*nb_inplace_add*/ 0," << endl; + s << INDENT << "/*nb_inplace_subtract*/ 0," << endl; + s << INDENT << "/*nb_inplace_multiply*/ 0," << endl; + s << INDENT << "/*nb_inplace_divide*/ 0," << 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::writeFlagsDefinition(QTextStream& s, const AbstractMetaEnum* cppEnum) +{ + FlagsTypeEntry* flagsEntry = cppEnum->typeEntry()->flags(); + if (!flagsEntry) + return; + QString cpythonName = cpythonFlagsName(flagsEntry); + QString enumName = cpythonEnumName(cppEnum); + + s << "// forward declaration of new function" << endl; + s << "static PyTypeObject " << cpythonName << "_Type = {" << endl; + s << INDENT << "PyObject_HEAD_INIT(&PyType_Type)" << endl; + s << INDENT << "/*ob_size*/ 0," << endl; + s << INDENT << "/*tp_name*/ \"" << flagsEntry->flagsName() << "\"," << endl; + s << INDENT << "/*tp_basicsize*/ 0," << endl; + s << INDENT << "/*tp_itemsize*/ 0," << endl; + s << INDENT << "/*tp_dealloc*/ 0," << 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*/ 0," << endl; + s << INDENT << "/*tp_as_number*/ " << enumName << "_Type.tp_as_number," << endl; + s << INDENT << "/*tp_as_sequence*/ 0," << endl; + s << INDENT << "/*tp_as_mapping*/ 0," << endl; + s << INDENT << "/*tp_hash*/ 0," << endl; + s << INDENT << "/*tp_call*/ 0," << endl; + s << INDENT << "/*tp_str*/ 0," << endl; + s << INDENT << "/*tp_getattro*/ 0," << endl; + s << INDENT << "/*tp_setattro*/ 0," << endl; + s << INDENT << "/*tp_as_buffer*/ 0," << endl; + s << INDENT << "/*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES," << endl; + s << INDENT << "/*tp_doc*/ 0," << endl; + s << INDENT << "/*tp_traverse*/ 0," << endl; + s << INDENT << "/*tp_clear*/ 0," << endl; + s << INDENT << "/*tp_richcompare*/ 0," << endl; + s << INDENT << "/*tp_weaklistoffset*/ 0," << endl; + s << INDENT << "/*tp_iter*/ 0," << endl; + s << INDENT << "/*tp_iternext*/ 0," << endl; + s << INDENT << "/*tp_methods*/ 0," << endl; + s << INDENT << "/*tp_members*/ 0," << endl; + s << INDENT << "/*tp_getset*/ 0," << endl; + s << INDENT << "/*tp_base*/ &PyInt_Type," << 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*/ 0," << endl; + s << INDENT << "/*tp_alloc*/ 0," << endl; + s << INDENT << "/*tp_new*/ PyInt_Type.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 << endl; +} + +void CppGenerator::writeFlagsBinaryOperator(QTextStream& s, const AbstractMetaEnum* cppEnum, + QString pyOpName, QString cppOpName) +{ + FlagsTypeEntry* flagsEntry = cppEnum->typeEntry()->flags(); + Q_ASSERT(flagsEntry); + + QString converter = "Shiboken::Converter<" + flagsEntry->originalName() + " >::"; + + s << "PyObject* " << cpythonEnumName(cppEnum) << "___" << pyOpName << "__(PyObject* self, PyObject* arg)" << endl; + s << '{' << endl; + + // We need to check the type of self because self and arg can be swapped + s << INDENT << "if (" << converter << "checkType(self) && " << converter << "checkType(arg))" << endl; + s << INDENT << '{' << endl; + { + Indentation indent(INDENT); + s << INDENT << "PyErr_Format(PyExc_TypeError, \"unsupported operand type(s) for %s: '%s' and '%s'\", \"" + << cppOpName << "\", self->ob_type->tp_name, arg->ob_type->tp_name);" << endl; + s << INDENT << "return 0;" << endl; + } + s << INDENT << '}' << endl << endl; + + s << INDENT << "return Shiboken::Converter< " << flagsEntry->originalName() << " >::toPython(" << endl; + { + Indentation indent(INDENT); + s << INDENT << "Shiboken::Converter<" << flagsEntry->originalName() << ">::toCpp(self)" << endl; + s << INDENT << cppOpName << " Shiboken::Converter< "; + s << flagsEntry->originalName() << " >::toCpp(arg)" << endl; + } + s << INDENT << ");" << 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); + + QString converter = "Shiboken::Converter<" + flagsEntry->originalName() + " >::"; + + s << "PyObject* " << cpythonEnumName(cppEnum) << "___" << pyOpName << "__(PyObject* self, PyObject* arg)" << endl; + s << '{' << endl; + s << INDENT << "return Shiboken::Converter< " << (boolResult ? "bool" : flagsEntry->originalName()); + s << " >::toPython(" << endl; + { + Indentation indent(INDENT); + s << INDENT << cppOpName << converter << "toCpp(self)" << endl; + } + s << INDENT << ");" << endl; + s << '}' << endl << endl; +} + +void CppGenerator::writeClassRegister(QTextStream& s, const AbstractMetaClass* metaClass) +{ + QString pyTypeName = cpythonTypeName(metaClass); + s << "void init_" << metaClass->qualifiedCppName().replace("::", "_") << "(PyObject* module)" << endl; + s << '{' << endl; + s << INDENT << cpythonTypeNameExt(metaClass->typeEntry()) << " = reinterpret_cast(&" << cpythonTypeName(metaClass->typeEntry()) << ");" << endl << endl; + + // class inject-code target/beginning + if (!metaClass->typeEntry()->codeSnips().isEmpty()) { + writeCodeSnips(s, metaClass->typeEntry()->codeSnips(), CodeSnip::Beginning, TypeSystem::TargetLangCode, 0, 0, metaClass); + s << endl; + } + + if (metaClass->baseClass()) + s << INDENT << pyTypeName << ".super.ht_type.tp_base = " << cpythonTypeNameExt(metaClass->baseClass()->typeEntry()) << ';' << endl; + // Multiple inheritance + const AbstractMetaClassList baseClasses = getBaseClasses(metaClass); + if (metaClass->baseClassNames().size() > 1) { + s << INDENT << pyTypeName << ".super.ht_type.tp_bases = PyTuple_Pack("; + s << baseClasses.size(); + s << ',' << endl; + QStringList bases; + foreach (const AbstractMetaClass* base, baseClasses) + bases << "(PyTypeObject*)"+cpythonTypeNameExt(base->typeEntry()); + Indentation indent(INDENT); + s << INDENT << bases.join(", ") << ");" << endl << endl; + } + + // Fill multiple inheritance init function, if needed. + const AbstractMetaClass* miClass = getMultipleInheritingClass(metaClass); + if (miClass && miClass != metaClass) { + s << INDENT << cpythonTypeName(metaClass) << ".mi_init = "; + s << "reinterpret_cast(" + cpythonTypeNameExt(miClass->typeEntry()) + ")->mi_init;" << endl << endl; + } + + // Set typediscovery struct or fill the struct of another one + if (metaClass->isPolymorphic()) { + s << INDENT << "// Fill type discovery information" << endl; + if (metaClass->baseClass()) { + s << INDENT << cpythonTypeName(metaClass) << ".type_discovery = &" << cpythonBaseName(metaClass) << "_typeDiscovery;" << endl; + s << INDENT << "Shiboken::BindingManager& bm = Shiboken::BindingManager::instance();" << endl; + foreach (const AbstractMetaClass* base, baseClasses) { + s << INDENT << "bm.addClassInheritance(reinterpret_cast(" << cpythonTypeNameExt(base->typeEntry()) << "), &" << cpythonTypeName(metaClass) << ");" << endl; + } + } + s << endl; + } + + s << INDENT << "if (PyType_Ready((PyTypeObject*)&" << pyTypeName << ") < 0)" << endl; + s << INDENT << INDENT << "return;" << endl << endl; + + if (metaClass->enclosingClass() && (metaClass->enclosingClass()->typeEntry()->codeGeneration() != TypeEntry::GenerateForSubclass) ) { + s << INDENT << "PyDict_SetItemString(module," + << "\"" << metaClass->name() << "\", (PyObject*)&" << pyTypeName << ");" << endl; + } else { + s << INDENT << "Py_INCREF(reinterpret_cast(&" << pyTypeName << "));" << endl; + s << INDENT << "PyModule_AddObject(module, \"" << metaClass->name() << "\"," << endl; + Indentation indent(INDENT); + s << INDENT << "((PyObject*)&" << pyTypeName << "));" << endl << endl; + } + + if (!metaClass->enums().isEmpty()) { + s << INDENT << "// Initialize enums" << endl; + s << INDENT << "PyObject* enum_item;" << endl << endl; + } + + foreach (const AbstractMetaEnum* cppEnum, metaClass->enums()) + writeEnumInitialization(s, cppEnum); + + 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 (!metaClass->typeEntry()->codeSnips().isEmpty()) { + s << endl; + writeCodeSnips(s, metaClass->typeEntry()->codeSnips(), CodeSnip::End, TypeSystem::TargetLangCode, 0, 0, metaClass); + } + + if (!metaClass->isNamespace()) { + bool isObjectType = metaClass->typeEntry()->isObject(); + QString typeName = metaClass->qualifiedCppName(); + if (!isObjectType) + s << INDENT << "Shiboken::TypeResolver::createValueTypeResolver<" << typeName << " >" << "(\"" << typeName << "\");\n"; + + s << INDENT << "Shiboken::TypeResolver::createObjectTypeResolver<" << typeName << " >" << "(\"" << typeName << "*\");\n"; + + QString functionSufix = (isObjectType ? "Object" : "Value"); + s << INDENT << "Shiboken::TypeResolver::create" << functionSufix; + s << "TypeResolver<" << typeName << " >" << "(typeid(" << typeName << ").name());\n"; + if (shouldGenerateCppWrapper(metaClass)) { + s << INDENT << "Shiboken::TypeResolver::create" << functionSufix; + s << "TypeResolver<" << typeName << " >" << "(typeid(" << wrapperName(metaClass) << ").name());\n"; + } + } + + if (usePySideExtensions() && !metaClass->isNamespace()) { + // Qt metatypes are registered only on their first use, so we do this now. + const char* star = metaClass->typeEntry()->isObject() ? "*" : ""; + s << INDENT << "PySide::initQtMetaType<" << metaClass->qualifiedCppName() << star << " >();" << endl; + } + + s << '}' << endl << endl; +} + +void CppGenerator::writeTypeDiscoveryFunction(QTextStream& s, const AbstractMetaClass* metaClass) +{ + QString polymorphicExpr = metaClass->typeEntry()->polymorphicIdValue(); + + s << "static SbkBaseWrapperType* " << cpythonBaseName(metaClass) << "_typeDiscovery(void* cptr, SbkBaseWrapperType* instanceType)\n{" << endl; + + if (!metaClass->baseClass()) { + s << INDENT << "TypeResolver* typeResolver = TypeResolver::get(typeid(*reinterpret_cast<" + << metaClass->qualifiedCppName() << "*>(cptr)).name());" << endl; + s << INDENT << "if (typeResolver)" << endl; + { + Indentation indent(INDENT); + s << INDENT << "return reinterpret_cast(typeResolver->pythonType());" << endl; + } + } else if (!polymorphicExpr.isEmpty()) { + polymorphicExpr = polymorphicExpr.replace("%1", " reinterpret_cast<"+metaClass->qualifiedCppName()+"*>(cptr)"); + s << INDENT << " if (" << polymorphicExpr << ")" << endl; + { + Indentation indent(INDENT); + s << INDENT << "return &" << cpythonTypeName(metaClass) << ';' << 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() << " >()) && dynamic_cast<" << metaClass->qualifiedCppName() + << "*>(reinterpret_cast<"<< ancestor->qualifiedCppName() << "*>(cptr)))" << endl; + Indentation indent(INDENT); + s << INDENT << "return &" << cpythonTypeName(metaClass) << ';' << 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-expresison 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* self, PyObject* name, PyObject* value)" << endl; + s << '{' << endl; + if (usePySideExtensions()) { + s << INDENT << "Shiboken::AutoDecRef pp(PySide::qproperty_get_object(self, name));" << endl; + s << INDENT << "if (!pp.isNull())" << endl; + Indentation indent(INDENT); + s << INDENT << INDENT << "return PySide::qproperty_set(pp, self, value);" << endl; + } + s << INDENT << "return PyObject_GenericSetAttr(self, name, value);" << endl; + s << '}' << endl; +} + +void CppGenerator::writeGetattroFunction(QTextStream& s, const AbstractMetaClass* metaClass) +{ + s << "static PyObject* " << cpythonGetattroFunctionName(metaClass) << "(PyObject* self, PyObject* name)" << endl; + s << '{' << endl; + if (classNeedsGetattroFunction(metaClass)) { + s << INDENT << "if (self) {" << endl; + { + Indentation indent(INDENT); + s << INDENT << "if (SbkBaseWrapper_instanceDict(self)) {" << endl; + { + Indentation indent(INDENT); + s << INDENT << "PyObject* meth = PyDict_GetItem(SbkBaseWrapper_instanceDict(self), 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 << "const char* cname = PyString_AS_STRING(name);" << endl; + foreach (const AbstractMetaFunction* func, getMethodsWithBothStaticAndNonStaticMethods(metaClass)) { + s << INDENT << "if (strcmp(cname, \"" << func->name() << "\") == 0)" << endl; + Indentation indent(INDENT); + s << INDENT << "return PyCFunction_NewEx(&" << cpythonMethodDefinitionName(func) << ", self, 0);" << endl; + } + } + s << INDENT << '}' << endl; + } + s << INDENT << "PyObject* attr = PyObject_GenericGetAttr(self, name);" << endl; + if (usePySideExtensions() && metaClass->isQObject()) { + s << INDENT << "if (attr && PySide::isQPropertyType(attr)) {" << endl; + { + Indentation indent(INDENT); + s << INDENT << "PyObject *value = PySide::qproperty_get(attr, self);" << endl; + s << INDENT << "if (!value)" << endl; + { + Indentation indentation(INDENT); + s << INDENT << "return " << m_currentErrorCode << ';' << endl; + } + s << INDENT << "Py_DECREF(attr);" << endl; + s << INDENT << "Py_INCREF(value);" << endl; + s << INDENT << "attr = value;" << endl; + } + s << INDENT << "}" << endl; + } + s << INDENT << "return attr;" << 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); + } + + foreach (const AbstractMetaClass* cls, classes()) { + 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); + if (file.open(QFile::WriteOnly)) { + QTextStream s(&file); + + // write license comment + s << licenseComment() << endl; + + s << "#include " << endl; + s << "#include " << endl; + s << "#include " << endl; + + s << "#include \"" << getModuleHeaderFileName() << '"' << endl << endl; + foreach (const Include& include, includes) + s << include; + s << endl; + + TypeSystemTypeEntry* moduleEntry = reinterpret_cast(TypeDatabase::instance()->findType(packageName())); + CodeSnipList snips; + if (moduleEntry) + snips = moduleEntry->codeSnips(); + + // module inject-code native/beginning + if (!snips.isEmpty()) { + writeCodeSnips(s, snips, CodeSnip::Beginning, TypeSystem::NativeCode); + 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()) + continue; + writeEnumDefinition(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; + } + } + + s << "PyTypeObject** " << cppApiVariableName() << ";" << endl << endl;; + foreach (const QString& requiredModule, TypeDatabase::instance()->requiredTargetImports()) + s << "PyTypeObject** " << cppApiVariableName(requiredModule) << ";" << endl << endl;; + + s << "// Module initialization "; + s << "------------------------------------------------------------" << endl; + ExtendedConverterData extendedConverters = getExtendedConverters(); + if (!extendedConverters.isEmpty()) + s << "// Extended Converters" << endl; + foreach (const TypeEntry* externalType, extendedConverters.keys()) { + writeExtendedIsConvertibleFunction(s, externalType, extendedConverters[externalType]); + writeExtendedToCppFunction(s, externalType, extendedConverters[externalType]); + s << endl; + } + 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 << "extern \"C\" SBK_EXPORT_MODULE void init" << moduleName() << "()" << endl; + s << '{' << endl; + + // module inject-code target/beginning + if (!snips.isEmpty()) { + writeCodeSnips(s, snips, CodeSnip::Beginning, TypeSystem::TargetLangCode); + s << endl; + } + + foreach (const QString& requiredModule, TypeDatabase::instance()->requiredTargetImports()) { + s << INDENT << "if (!Shiboken::importModule(\"" << requiredModule << "\", &" << cppApiVariableName(requiredModule) << ")) {" << endl; + s << INDENT << INDENT << "PyErr_SetString(PyExc_ImportError," << "\"could not import "; + s << requiredModule << "\");" << endl << INDENT << INDENT << "return;" << endl; + s << INDENT << "}" << endl << endl; + } + + s << INDENT << "Shiboken::initShiboken();" << endl; + s << INDENT << "PyObject* module = Py_InitModule(\"" << moduleName() << "\", "; + s << moduleName() << "_methods);" << endl << endl; + + s << INDENT << "// Create a CObject containing the API pointer array's address" << endl; + s << INDENT << "static PyTypeObject* cppApi[" << "SBK_" << moduleName() << "_IDX_COUNT" << "];" << endl; + s << INDENT << cppApiVariableName() << " = cppApi;" << endl; + s << INDENT << "PyObject* cppApiObject = PyCObject_FromVoidPtr(reinterpret_cast(cppApi), 0);" << endl; + s << INDENT << "PyModule_AddObject(module, \"_Cpp_Api\", cppApiObject);" << endl << endl; + s << INDENT << "// Initialize classes in the type system" << endl; + s << classPythonDefines; + + if (!extendedConverters.isEmpty()) { + s << INDENT << "// Initialize extended Converters" << endl; + s << INDENT << "Shiboken::SbkBaseWrapperType* shiboType;" << endl << endl; + } + foreach (const TypeEntry* externalType, extendedConverters.keys()) { + writeExtendedConverterInitialization(s, externalType, extendedConverters[externalType]); + s << endl; + } + s << endl; + + if (!globalEnums().isEmpty()) { + s << INDENT << "// Initialize enums" << endl; + s << INDENT << "PyObject* enum_item;" << endl << endl; + } + + foreach (const AbstractMetaEnum* cppEnum, globalEnums()) + writeEnumInitialization(s, cppEnum); + + // Register primitive types on TypeResolver + s << INDENT << "// Register primitive types on TypeResolver" << endl; + foreach(const PrimitiveTypeEntry* pte, primitiveTypes()) { + if (pte->generateCode()) + s << INDENT << "Shiboken::TypeResolver::createValueTypeResolver<" << pte->name() << " >(\"" << pte->name() << "\");" << endl; + } + // 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); + typeResolvers << QMetaObject::normalizedType(value.toAscii().constData()); + } + } + } + } + } + foreach (QString type, typeResolvers) + s << INDENT << "Shiboken::TypeResolver::createValueTypeResolver<" << type << " >(\"" << type << "\");" << 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; + } + + s << '}' << endl << endl; + + // module inject-code native/end + if (!snips.isEmpty()) { + writeCodeSnips(s, snips, CodeSnip::End, TypeSystem::NativeCode); + s << endl; + } + } +} + +bool CppGenerator::writeParentChildManagement(QTextStream& s, const AbstractMetaFunction* func, int argIndex, bool useHeuristicPolicy) +{ + const int numArgs = func->arguments().count(); + const AbstractMetaClass* cppClass = func->ownerClass(); + const AbstractMetaClass* dClass = func->declaringClass(); + bool ctorHeuristicEnabled = func->isConstructor() && useCtorHeuristic() && useHeuristicPolicy; + + QString parentVariable; + QString childVariable; + ArgumentOwner argOwner = func->argumentOwner(cppClass, argIndex); + + if (argOwner.index == -2) //invalid + argOwner = func->argumentOwner(dClass, argIndex); + + bool usePyArgs = pythonFunctionWrapperUsesListOfArguments(OverloadData(getFunctionGroups(func->implementingClass())[func->name()], this)); + + 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" && (arg->type()->isObject() || arg->type()->isQObject())) { + action = ArgumentOwner::Add; + parentIndex = argIndex; + childIndex = -1; + } + } + + 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 = "self"; + else + parentVariable = usePyArgs ? "pyargs["+QString::number(parentIndex-1)+"]" : "arg"; + } + + if (childIndex == 0) + childVariable = PYTHON_RETURN_VAR; + else if (childIndex == -1) + childVariable = "self"; + else + childVariable = usePyArgs ? "pyargs["+QString::number(childIndex-1)+"]" : "arg"; + + s << INDENT << "Shiboken::setParent(" << parentVariable << ", " << childVariable << ");\n"; + + return true; + } + + if (argIndex == 0 && useHeuristicPolicy) + writeReturnValueHeuristics(s, func); + + 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, i == 0 ? useHeuristicForReturn : true); +} + +void CppGenerator::writeReturnValueHeuristics(QTextStream& s, const AbstractMetaFunction* func, const QString& self) +{ + AbstractMetaType *type = func->type(); + if (!useReturnValueHeuristic() + || !func->ownerClass() + || !type + || func->isStatic() + || !func->typeReplaced(0).isEmpty()) { + return; + } + + if (type->isQObject() || type->isObject() || type->isValuePointer()) + s << INDENT << "Shiboken::setParent(" << self << ", " PYTHON_RETURN_VAR ");" << endl; +} + +void CppGenerator::writeHashFunction(QTextStream& s, const AbstractMetaClass* metaClass) +{ + s << "static long " << cpythonBaseName(metaClass) << "_HashFunc(PyObject* obj)"; + s << '{' << endl; + s << INDENT << "return " << metaClass->typeEntry()->hashFunction() << '('; + writeToCppConversion(s, metaClass, "obj"); + s << ");" << endl; + s << '}' << endl << endl; +} + +void CppGenerator::writeObjCopierFunction(QTextStream& s, const AbstractMetaClass* metaClass) +{ + if (!(metaClass->typeEntry()->isValue() && shouldGenerateCppWrapper(metaClass))) + return; + s << "static void* " << cpythonBaseName(metaClass) << "_ObjCopierFunc(const void* ptr)"; + s << '{' << endl; + s << INDENT << "return new " << wrapperName(metaClass) << "(*reinterpret_castqualifiedCppName() << "*>(ptr));\n"; + s << '}' << endl << endl; + +} + +void CppGenerator::writeStdListWrapperMethods(QTextStream& s, const AbstractMetaClass* metaClass) +{ + //len + s << "Py_ssize_t " << cpythonBaseName(metaClass->typeEntry()) << "__len__" << "(PyObject* self)" << endl << '{' << endl; + s << INDENT << "if (Shiboken::cppObjectIsInvalid(self))" << endl; + s << INDENT << INDENT << "return 0;" << endl << endl; + s << INDENT << metaClass->qualifiedCppName() << " &cppSelf = Shiboken::Converter<" << metaClass->qualifiedCppName() <<"& >::toCpp(self);" << endl; + s << INDENT << "return cppSelf.size();" << endl; + s << "}" << endl; + + //getitem + s << "PyObject* " << cpythonBaseName(metaClass->typeEntry()) << "__getitem__" << "(PyObject* self, Py_ssize_t _i)" << endl << '{' << endl; + s << INDENT << "if (Shiboken::cppObjectIsInvalid(self))" << endl; + s << INDENT << INDENT << "return 0;" << endl << endl; + s << INDENT << metaClass->qualifiedCppName() << " &cppSelf = Shiboken::Converter<" << metaClass->qualifiedCppName() <<"& >::toCpp(self);" << endl; + s << INDENT << "if (_i < 0 || _i >= (Py_ssize_t) cppSelf.size()) {" << endl; + s << INDENT << INDENT << "PyErr_SetString(PyExc_IndexError, \"index out of bounds\");" << endl; + s << INDENT << INDENT << "return 0;" << endl << INDENT << "}" << endl; + s << INDENT << metaClass->qualifiedCppName() << "::iterator _item = cppSelf.begin();" << endl; + s << INDENT << "for(Py_ssize_t pos=0; pos < _i; pos++) _item++;" << endl; + s << INDENT << "return Shiboken::Converter<" << metaClass->qualifiedCppName() << "::value_type>::toPython(*_item);" << endl; + s << "}" << endl; + + //setitem + s << "int " << cpythonBaseName(metaClass->typeEntry()) << "__setitem__" << "(PyObject* self, Py_ssize_t _i, PyObject* _value)" << endl << '{' << endl; + s << INDENT << "if (Shiboken::cppObjectIsInvalid(self))" << endl; + s << INDENT << INDENT << "return -1;" << endl; + s << INDENT << metaClass->qualifiedCppName() << " &cppSelf = Shiboken::Converter<" << metaClass->qualifiedCppName() <<"& >::toCpp(self);" << endl; + s << INDENT << "if (_i < 0 || _i >= (Py_ssize_t) cppSelf.size()) {" << endl; + s << INDENT << INDENT << "PyErr_SetString(PyExc_IndexError, \"list assignment index out of range\");" << endl; + s << INDENT << INDENT << "return -1;" << endl << INDENT << "}" << endl; + s << INDENT << metaClass->qualifiedCppName() << "::iterator _item = cppSelf.begin();" << endl; + s << INDENT << "for(Py_ssize_t pos=0; pos < _i; pos++) _item++;" << endl; + + s << INDENT << metaClass->qualifiedCppName() << "::value_type cppValue = Shiboken::Converter<" << metaClass->qualifiedCppName() << "::value_type>::toCpp(_value);" << endl; + s << INDENT << "*_item = cppValue;" << endl; + s << INDENT << "return 0;"; + s << endl << "}" << endl; +} + + diff --git a/generator/cppgenerator.h b/generator/cppgenerator.h new file mode 100644 index 000000000..b4d68dc45 --- /dev/null +++ b/generator/cppgenerator.h @@ -0,0 +1,201 @@ +/* + * 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); + void writeVirtualMethodNative(QTextStream& s, const AbstractMetaFunction* func); + void writeMetaObjectMethod(QTextStream& s, const AbstractMetaClass* metaClass); + + void writeConstructorWrapper(QTextStream &s, const AbstractMetaFunctionList overloads); + void writeDestructorWrapper(QTextStream& s, const AbstractMetaClass* metaClass); + void writeMinimalConstructorCallArguments(QTextStream& s, const AbstractMetaClass* metaClass); + void writeMinimalConstructorCallArguments(QTextStream& s, const AbstractMetaType* type); + void writeMethodWrapper(QTextStream &s, const AbstractMetaFunctionList overloads); + void writeArgumentsInitializer(QTextStream& s, OverloadData& overloadData); + void writeCppSelfDefinition(QTextStream& s, const AbstractMetaFunction* func); + + void writeErrorSection(QTextStream& s, OverloadData& overloadData); + /** + * Writes the check section for the validity of wrapped C++ objects. + * \param s text stream to write + * \param argName Python argument name + * \param type the TypeEntry passed when the validity check must confirm the type of the Python wrapper to be checked + */ + void writeInvalidCppObjectCheck(QTextStream& s, QString pyArgName = "self", const TypeEntry* type = 0); + void writeTypeCheck(QTextStream& s, const AbstractMetaType* argType, QString argumentName, bool isNumber = false, QString customType = ""); + 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 + */ + void writeArgumentConversion(QTextStream& s, const AbstractMetaType* argType, + QString argName, QString pyArgName, + const AbstractMetaClass* context = 0, + QString defaultValue = QString()); + /// Convenience method to call writeArgumentConversion with an AbstractMetaArgument instead of an AbstractMetaType. + void writeArgumentConversion(QTextStream& s, const AbstractMetaArgument* arg, + QString argName, QString pyArgName, + const AbstractMetaClass* context = 0, + QString defaultValue = QString()) + { + writeArgumentConversion(s, arg->type(), argName, pyArgName, context, defaultValue); + } + + /** + * 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 = 0); + 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); + /// Writes the struct PySequenceMethods for types thats supports the python sequence protocol + void writeTypeAsSequenceDefinition(QTextStream& s, const AbstractMetaClass* metaClass); + void writeTypeAsNumberDefinition(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 writeEnumNewMethod(QTextStream& s, const AbstractMetaEnum* cppEnum); + void writeEnumDefinition(QTextStream& s, const AbstractMetaEnum* metaEnum); + void writeEnumInitialization(QTextStream& s, const AbstractMetaEnum* metaEnum); + + void writeSignalInitialization(QTextStream& s, const AbstractMetaClass* metaClass); + + void writeFlagsDefinition(QTextStream& s, const AbstractMetaEnum* cppEnum); + void writeFlagsMethods(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 mulltiple inheritance. + void writeSpecialCastFunction(QTextStream& s, const AbstractMetaClass* metaClass); + + void writeExtendedIsConvertibleFunction(QTextStream& s, const TypeEntry* externalType, const QList& conversions); + void writeExtendedToCppFunction(QTextStream& s, const TypeEntry* externalType, const QList& conversions); + 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 = "self"); + /** + * 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 casted. + QStringList getAncestorMultipleInheritance(const AbstractMetaClass* metaClass); + + /// Returns true if the given class supports the python sequence protocol + bool supportsSequenceProtocol(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); + void writeObjCopierFunction(QTextStream& s, const AbstractMetaClass* metaClass); + + // Write default implementations for sequence protocol + void writeStdListWrapperMethods(QTextStream& s, const AbstractMetaClass* metaClass); + + // Maps special function names to function parameters and return types + // used by CPython API in the sequence protocol. + QHash > m_sequenceProtocol; + int m_currentErrorCode; +}; + +#endif // CPPGENERATOR_H + diff --git a/generator/headergenerator.cpp b/generator/headergenerator.cpp new file mode 100644 index 000000000..2e060f429 --- /dev/null +++ b/generator/headergenerator.cpp @@ -0,0 +1,610 @@ +/* + * 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 + * + */ + +#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 +{ + QString fieldType = field->type()->cppSignature(); + QString fieldName = field->enclosingClass()->qualifiedCppName() + "::" + field->name(); + + s << INDENT << "inline " << fieldType << ' ' << protectedFieldGetterName(field) << "()"; + s << " { return " << fieldName << "; }" << endl; + s << INDENT << "inline void " << protectedFieldSetterName(field) << '(' << fieldType << " value)"; + s << " { " << fieldName << " = value; }" << endl; +} + +void HeaderGenerator::generateClass(QTextStream& s, const AbstractMetaClass* metaClass) +{ + ReportHandler::debugSparse("Generating header for " + metaClass->fullName()); + 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; + +#ifndef AVOID_PROTECTED_HACK + s << "#define protected public" << endl << endl; +#endif + + 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; + + if (metaClass->typeEntry()->isValue()) + writeCopyCtor(s, metaClass); + + bool hasVirtualFunction = false; + foreach (AbstractMetaFunction *func, filterFunctions(metaClass)) { + if (func->isVirtual()) + hasVirtualFunction = true; + writeFunction(s, func); + } + +#ifdef AVOID_PROTECTED_HACK + if (metaClass->hasProtectedFields()) { + foreach (AbstractMetaField* field, metaClass->fields()) { + if (!field->isProtected()) + continue; + writeProtectedFieldAccessors(s, field); + } + } +#endif + + //destructor +#ifdef AVOID_PROTECTED_HACK + if (!metaClass->hasPrivateDestructor()) +#endif + s << INDENT << (metaClass->hasVirtualDestructor() || hasVirtualFunction ? "virtual " : "") << "~" << wrapperName << "();" << endl; + + writeCodeSnips(s, metaClass->typeEntry()->codeSnips(), CodeSnip::Declaration, TypeSystem::NativeCode); + +#ifdef AVOID_PROTECTED_HACK + if (!metaClass->hasPrivateDestructor()) { +#endif + + if (usePySideExtensions() && metaClass->isQObject()) { + s << "public:\n"; + s << INDENT << "virtual int qt_metacall(QMetaObject::Call call, int id, void** args);\n"; + s << "private:\n"; + s << INDENT << "mutable PySide::DynamicQMetaObject* m_metaObject;\n"; + } + +#ifdef AVOID_PROTECTED_HACK + } +#endif + + s << "};" << endl << endl; + } + + s << "#endif // SBK_" << headerGuard << "_H" << endl << endl; +} + +void HeaderGenerator::writeFunction(QTextStream& s, const AbstractMetaFunction* func) const +{ + // do not write copy ctors here. + if (func->isCopyConstructor()) + return; + + if (func->isConstructor() && func->isUserAdded()) + return; + +#ifdef AVOID_PROTECTED_HACK + if (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; + } +#endif + + // pure virtual functions need a default implementation + if ((func->isPrivate() && !visibilityModifiedToPrivate(func)) + || (func->isModifiedRemoved() && !func->isAbstract())) + return; + +#ifdef AVOID_PROTECTED_HACK + if (func->ownerClass()->hasPrivateDestructor() && (func->isAbstract() || func->isVirtual())) + return; +#endif + + 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; + + // TODO: when modified an abstract method ceases to be virtual but stays abstract + //if (func->isModifiedRemoved() && func->isAbstract()) { + //} + } +} + +void HeaderGenerator::writeTypeConverterDecl(QTextStream& s, const TypeEntry* type) +{ + s << "template<>" << endl; + + const AbstractMetaClass* metaClass = classes().findClass(type->name()); + bool isAbstractOrObjectType = (metaClass && metaClass->isAbstract()) || type->isObject(); + + AbstractMetaFunctionList implicitConvs; + foreach (AbstractMetaFunction* func, implicitConversions(type)) { + if (!func->isUserAdded()) + implicitConvs << func; + } + bool isValueTypeWithImplConversions = type->isValue() && !implicitConvs.isEmpty(); + bool hasCustomConversion = type->hasConversionRule(); + QString typeT = type->name() + (isAbstractOrObjectType ? "*" : ""); + QString typeName = type->name(); + +#ifdef AVOID_PROTECTED_HACK + const AbstractMetaEnum* metaEnum = findAbstractMetaEnum(type); + if (metaEnum && metaEnum->isProtected()) { + typeT = protectedEnumSurrogateName(metaEnum); + typeName = typeT; + } +#endif + + s << "struct Converter<" << typeT << " >"; + if (!hasCustomConversion) { + if (type->isEnum()) + s << " : EnumConverter"; + else if (type->isFlags()) + s << " : QFlagsConverter"; + else if (isAbstractOrObjectType) + s << " : ObjectTypeConverter"; + else + s << " : ValueTypeConverter"; + s << '<' << typeName << " >"; + } + s << endl << '{' << endl; + if (isValueTypeWithImplConversions || hasCustomConversion) { + s << INDENT << "static " << type->name() << " toCpp(PyObject* pyobj);" << endl; + s << INDENT << "static bool isConvertible(PyObject* pyobj);" << endl; + if (hasCustomConversion) { + s << INDENT << "static bool checkType(PyObject* pyobj);" << endl; + s << INDENT << "static inline PyObject* toPython(void* cppObj) { return toPython(*reinterpret_cast<" + << type->name() << (isAbstractOrObjectType ? "" : "*") << " >(cppObj)); }" << endl; + s << INDENT << "static PyObject* toPython(const " << type->name() << "& cppObj);" << endl; + } + } + s << "};" << endl; + + // write value-type like converter to object-types + if (isAbstractOrObjectType) { + s << endl << "template<>" << endl; + s << "struct Converter<" << type->name() << "& > : ObjectTypeReferenceConverter<" << type->name() << " >" << endl << '{' << endl; + s << "};" << endl; + } +} + +void HeaderGenerator::writeTypeIndexDefineLine(QTextStream& s, const TypeEntry* typeEntry, int& idx) +{ + if (!typeEntry || !typeEntry->generateCode()) + return; + s.setFieldAlignment(QTextStream::AlignLeft); + s << "#define "; + s.setFieldWidth(60); + s << getTypeIndexVariableName(typeEntry); + s.setFieldWidth(0); + s << ' ' << (idx++) << endl; + if (typeEntry->isEnum()) { + const EnumTypeEntry* ete = reinterpret_cast(typeEntry); + if (ete->flags()) + writeTypeIndexDefineLine(s, ete->flags(), idx); + } +} + +void HeaderGenerator::writeTypeIndexDefine(QTextStream& s, const AbstractMetaClass* metaClass, int& idx) +{ + if (!metaClass->typeEntry()->generateCode()) + return; + writeTypeIndexDefineLine(s, metaClass->typeEntry(), idx); + foreach (const AbstractMetaEnum* metaEnum, metaClass->enums()) + writeTypeIndexDefineLine(s, metaEnum->typeEntry(), idx); +} + +void HeaderGenerator::finishGeneration() +{ + if (classes().isEmpty()) + return; + + // 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 convertersDecl; + QTextStream convDecl(&convertersDecl); + QString sbkTypeFunctions; + QTextStream typeFunctions(&sbkTypeFunctions); + QString converterImpl; + QTextStream convImpl(&converterImpl); + + Indentation indent(INDENT); + + macrosStream << "// Type indices" << endl; + int idx = 0; + foreach (const AbstractMetaClass* metaClass, classes()) + writeTypeIndexDefine(macrosStream, metaClass, idx); + foreach (const AbstractMetaEnum* metaEnum, globalEnums()) + writeTypeIndexDefineLine(macrosStream, metaEnum->typeEntry(), idx); + macrosStream << "#define "; + macrosStream.setFieldWidth(60); + macrosStream << "SBK_"+moduleName()+"_IDX_COUNT"; + macrosStream.setFieldWidth(0); + macrosStream << ' ' << idx << endl << endl; + macrosStream << "// This variable stores all python types exported by this module" << endl; + macrosStream << "extern PyTypeObject** " << cppApiVariableName() << ';' << endl << endl; + + macrosStream << "// Macros for type check" << endl; + foreach (const AbstractMetaEnum* cppEnum, globalEnums()) { + if (cppEnum->isAnonymous()) + continue; + includes << cppEnum->typeEntry()->include(); + writeTypeConverterDecl(convDecl, cppEnum->typeEntry()); + convDecl << endl; + 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()) + continue; + EnumTypeEntry* enumType = cppEnum->typeEntry(); + includes << enumType->include(); + writeTypeConverterDecl(convDecl, enumType); + FlagsTypeEntry* flagsEntry = enumType->flags(); + if (flagsEntry) + writeTypeConverterDecl(convDecl, flagsEntry); + convDecl << endl; + writeSbkTypeFunction(typeFunctions, cppEnum); + } + + if (!metaClass->isNamespace()) { + writeSbkTypeFunction(typeFunctions, metaClass); + writeSbkCopyCppObjectFunction(convDecl, metaClass); + writeTypeConverterDecl(convDecl, classType); + writeTypeConverterImpl(convImpl, classType); + convDecl << endl; + } + } + + 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; + #ifndef AVOID_PROTECTED_HACK + s << "//workaround to access protected functions" << endl; + s << "#define protected public" << endl << endl; + #endif + + 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; + + s << "namespace Shiboken" << endl << '{' << endl << endl; + + s << "// PyType functions, to get the PyObjectType for a type T\n"; + s << sbkTypeFunctions << endl; + + if (usePySideExtensions()) { + foreach (AbstractMetaClass* metaClass, classes()) { + if (!metaClass->isQObject() || !metaClass->typeEntry()->generateCode()) + continue; + + s << "template<>" << endl; + s << "inline PyObject* createWrapper<" << metaClass->qualifiedCppName() << " >(const "; + s << metaClass->qualifiedCppName() << "* cppobj, bool hasOwnership, bool isExactType)" << endl; + s << '{' << endl; + s << INDENT << "PyObject* pyObj = Shiboken::SbkBaseWrapper_New(reinterpret_cast(SbkType<" << metaClass->qualifiedCppName() << " >())," + << "const_cast<" << metaClass->qualifiedCppName() << "*>(cppobj), hasOwnership, isExactType);" << endl; + s << INDENT << "PySide::signalUpdateSource(pyObj);" << endl; + s << INDENT << "return pyObj;" << endl; + s << '}' << endl; + + } + } + + s << "// Generated converters declarations ----------------------------------" << endl << endl; + s << convertersDecl; + s << "} // namespace Shiboken" << endl << endl; + + s << "// User defined converters --------------------------------------------" << endl; + foreach (TypeEntry* typeEntry, TypeDatabase::instance()->entries()) { + if (typeEntry->hasConversionRule()) { + s << "// Conversion rule for: " << typeEntry->name() << endl; + s << typeEntry->conversionRule(); + } + } + s << "// Generated converters implemantations -------------------------------" << endl << endl; + s << converterImpl << endl; + + s << "#endif // " << includeShield << endl << endl; +} + +void HeaderGenerator::writeSbkTypeFunction(QTextStream& s, const AbstractMetaEnum* cppEnum) +{ + QString enumName = cppEnum->name(); + if (cppEnum->enclosingClass()) + enumName = cppEnum->enclosingClass()->qualifiedCppName() + "::" + enumName; +#ifdef AVOID_PROTECTED_HACK + if (cppEnum->isProtected()) { + enumName = protectedEnumSurrogateName(cppEnum); + s << "enum " << enumName << " {};" << endl; + } +#endif + + 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::writeSbkCopyCppObjectFunction(QTextStream& s, const AbstractMetaClass* metaClass) +{ + if (!metaClass->typeEntry()->isValue() || !shouldGenerateCppWrapper(metaClass)) + return; + QString className = metaClass->qualifiedCppName(); + s << "template <>" << endl; + s << "struct SbkTypeInfo<" << className << " >" << endl; + s << '{' << endl; + s << INDENT << "static const bool isCppWrapper = true;" << endl; + s << "};" << endl; +} + +void HeaderGenerator::writeTypeConverterImpl(QTextStream& s, const TypeEntry* type) +{ + if (type->hasConversionRule()) + return; + + QString pyTypeName = cpythonTypeName(type); + + AbstractMetaFunctionList implicitConvs; + foreach (AbstractMetaFunction* func, implicitConversions(type)) { + if (!func->isUserAdded()) + implicitConvs << func; + } + + bool hasImplicitConversions = !implicitConvs.isEmpty(); + + // A specialized Converter::toCpp method is only need for + // classes with implicit conversions. + if (!hasImplicitConversions) + return; + + // Write Converter::isConvertible + s << "inline bool Shiboken::Converter<" << type->name() << " >::isConvertible(PyObject* pyobj)" << endl; + s << '{' << endl; + + if (type->isValue()) { + s << INDENT << "if (ValueTypeConverter<" << type->name() << " >::isConvertible(pyobj))" << endl; + Indentation indent(INDENT); + s << INDENT << "return true;" << endl; + } + + + s << INDENT << "SbkBaseWrapperType* shiboType = reinterpret_cast(SbkType<"; + s << type->name() << " >());" << endl; + s << INDENT << "return "; + bool isFirst = true; + foreach (const AbstractMetaFunction* ctor, implicitConvs) { + Indentation indent(INDENT); + if (isFirst) + isFirst = false; + else + s << endl << INDENT << " || "; + if (ctor->isConversionOperator()) + s << cpythonCheckFunction(ctor->ownerClass()->typeEntry()); + else + s << cpythonCheckFunction(ctor->arguments().first()->type()); + s << "(pyobj)"; + } + s << endl; + { + Indentation indent(INDENT); + s << INDENT << " || (shiboType->ext_isconvertible && shiboType->ext_isconvertible(pyobj));" << endl; + } + s << '}' << endl << endl; + + // Write Converter::toCpp function + s << "inline " << type->name() << " Shiboken::Converter<" << type->name() << " >::toCpp(PyObject* pyobj)" << endl; + s << '{' << endl; + + s << INDENT << "if (!Shiboken_TypeCheck(pyobj, " << type->name() << ")) {" << endl; + { + Indentation indent(INDENT); + s << INDENT << "SbkBaseWrapperType* shiboType = reinterpret_cast(SbkType<"; + s << type->name() << " >());" << endl; + } + bool firstImplicitIf = true; + foreach (const AbstractMetaFunction* ctor, implicitConvs) { + if (ctor->isModifiedRemoved()) + continue; + + Indentation indent(INDENT); + s << INDENT; + if (firstImplicitIf) + firstImplicitIf = false; + else + s << "else "; + + QString typeCheck; + QString toCppConv; + QTextStream tcc(&toCppConv); + if (ctor->isConversionOperator()) { + const AbstractMetaClass* metaClass = ctor->ownerClass(); + typeCheck = cpythonCheckFunction(metaClass->typeEntry()); + writeToCppConversion(tcc, metaClass, "pyobj"); + } else { + const AbstractMetaType* argType = ctor->arguments().first()->type(); + typeCheck = cpythonCheckFunction(argType); + writeToCppConversion(tcc, argType, 0, "pyobj"); + } + + s << "if (" << typeCheck << "(pyobj))" << endl; + { + Indentation indent(INDENT); + s << INDENT << "return " << type->name() << '(' << toCppConv << ");" << endl; + } + } + + { + Indentation indent(INDENT); + s << INDENT << "else if (shiboType->ext_isconvertible && shiboType->ext_tocpp && shiboType->ext_isconvertible(pyobj)) {" << endl; + { + Indentation indent(INDENT); + s << INDENT << type->name() << "* cptr = reinterpret_cast<" << type->name() << "*>(shiboType->ext_tocpp(pyobj));" << endl; + s << INDENT << "std::auto_ptr<" << type->name() << " > cptr_auto_ptr(cptr);" << endl; + s << INDENT << "return *cptr;" << endl; + } + s << INDENT << '}' << endl; + } + + s << INDENT << '}' << endl; + + s << INDENT << "return *" << cpythonWrapperCPtr(type, "pyobj") << ';' << endl; + s << '}' << endl << endl; +} + diff --git a/generator/headergenerator.h b/generator/headergenerator.h new file mode 100644 index 000000000..9802a104f --- /dev/null +++ b/generator/headergenerator.h @@ -0,0 +1,58 @@ +/* + * 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) const; + void writePureVirtualEmptyImpl(QTextStream& , const AbstractMetaFunction* func) const; + void writeDefaultImplementation(QTextStream& s, const AbstractMetaFunction* func) const; + void writeTypeConverterDecl(QTextStream& s, const TypeEntry* type); + void writeSbkTypeFunction(QTextStream& s, const AbstractMetaEnum* cppEnum); + void writeSbkTypeFunction(QTextStream& s, const AbstractMetaClass* cppClass); + void writeSbkCopyCppObjectFunction(QTextStream& s, const AbstractMetaClass* metaClass); + void writeTypeIndexDefineLine(QTextStream& s, const TypeEntry* typeEntry, int& idx); + void writeTypeIndexDefine(QTextStream& s, const AbstractMetaClass* metaClass, int& idx); + void writeTypeConverterImpl(QTextStream& s, const TypeEntry* type); + +}; + +#endif // HEADERGENERATOR_H + diff --git a/generator/main.cpp b/generator/main.cpp new file mode 100644 index 000000000..051225061 --- /dev/null +++ b/generator/main.cpp @@ -0,0 +1,34 @@ +/* + * 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 + +int main(int argc, char *argv[]) +{ + QStringList args; + args.append("--generatorSet=shiboken"); + for (int i = 1; i < argc; i++) + args.append(argv[i]); + return QProcess::execute("generatorrunner", args); +} + diff --git a/generator/overloaddata.cpp b/generator/overloaddata.cpp new file mode 100644 index 000000000..a71091ea3 --- /dev/null +++ b/generator/overloaddata.cpp @@ -0,0 +1,788 @@ +/* + * 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" + +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; +} + +/** + * 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() +{ + QHash map; // type_name -> id + QHash reverseMap; // id -> type_name + bool checkPyObject = false; + int pyobjectIndex = 0; + + // sort the children overloads + foreach(OverloadData *ov, m_nextOverloadData) { + ov->sortNextOverloads(); + } + + if (m_nextOverloadData.size() <= 1) + return; + + // Creates the 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. + int i = 0; + foreach(OverloadData* ov, m_nextOverloadData) { + const TypeEntry* typeEntry = getAliasedTypeEntry(ov->argType()->typeEntry()); + map[typeEntry->name()] = i; + reverseMap[i] = ov; + + if (!checkPyObject && typeEntry->name().contains("PyObject")) { + checkPyObject = true; + pyobjectIndex = i; + } + i++; + } + + // Create the graph of type dependencies based on implicit conversions. + Graph graph(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 int numPrimitives = sizeof(primitiveTypes)/sizeof(const char*); + bool hasPrimitive[numPrimitives]; + for (int i = 0; i < numPrimitives; ++i) + hasPrimitive[i] = map.contains(primitiveTypes[i]); + // just some alias + bool haveInt = hasPrimitive[0]; + bool haveLong = hasPrimitive[2]; + bool haveShort = hasPrimitive[4]; + + foreach(OverloadData* ov, m_nextOverloadData) { + const AbstractMetaType* targetType = ov->argType(); + const TypeEntry* targetTypeEntry = getAliasedTypeEntry(targetType->typeEntry()); + + foreach(AbstractMetaFunction* function, m_generator->implicitConversions(ov->argType())) { + QString convertibleType; + if (function->isConversionOperator()) + convertibleType = function->ownerClass()->typeEntry()->name(); + else + convertibleType = function->arguments().first()->type()->typeEntry()->name(); + + if (!map.contains(convertibleType)) + continue; + + int targetTypeId = map[targetTypeEntry->name()]; + int convertibleTypeId = 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); + } + + if (targetType->hasInstantiations()) { + foreach(const AbstractMetaType *instantiation, targetType->instantiations()) { + if (map.contains(instantiation->typeEntry()->name())) { + int target = map[targetTypeEntry->name()]; + int convertible = map[instantiation->typeEntry()->name()]; + + if (!graph.containsEdge(target, convertible)) // Avoid cyclic dependency. + graph.addEdge(convertible, target); + } + } + } + + /* Add dependency on PyObject, so its check is the last one (too generic) */ + if (checkPyObject && !targetTypeEntry->name().contains("PyObject")) { + graph.addEdge(map[targetTypeEntry->name()], pyobjectIndex); + } + + if (targetTypeEntry->isEnum()) { + for (int i = 0; i < numPrimitives; ++i) { + if (hasPrimitive[i]) + graph.addEdge(map[targetTypeEntry->name()], map[primitiveTypes[i]]); + } + } + } + + // Special case for double(int i) (not tracked by m_generator->implicitConversions + if (haveInt) { + if (map.contains("float")) + graph.addEdge(map["float"], map["int"]); + if (map.contains("double")) + graph.addEdge(map["double"], map["int"]); + if (map.contains("bool")) + graph.addEdge(map["bool"], map["int"]); + } + + if (haveShort) { + if (map.contains("float")) + graph.addEdge(map["float"], map["short"]); + if (map.contains("double")) + graph.addEdge(map["double"], map["short"]); + if (map.contains("bool")) + graph.addEdge(map["bool"], map["short"]); + } + + if (haveLong) { + if (map.contains("float")) + graph.addEdge(map["float"], map["long"]); + if (map.contains("double")) + graph.addEdge(map["double"], map["long"]); + if (map.contains("bool")) + graph.addEdge(map["bool"], map["long"]); + } + + // sort the overloads topologicaly based on the deps graph. + + QLinkedList unmappedResult = graph.topologicalSort(); + if (unmappedResult.isEmpty()) { + QString funcName = referenceFunction()->name(); + if (referenceFunction()->ownerClass()) + funcName.prepend(referenceFunction()->ownerClass()->name() + '.'); + ReportHandler::warning(QString("Cyclic dependency found on overloaddata for '%1' method!").arg(qPrintable(funcName))); + } + + m_nextOverloadData.clear(); + foreach(int i, unmappedResult) + m_nextOverloadData << 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 (!func->arguments()[i]->defaultValueExpression().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 && tmp->m_argType->typeEntry() == argType->typeEntry()) + || (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 (!func->arguments()[m_argPos + removedArgs]->defaultValueExpression().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 (!args[i]->defaultValueExpression().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 && !func->arguments()[j]->defaultValueExpression().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; + if (!arg->defaultValueExpression().isEmpty() || + arg->defaultValueExpression() != arg->originalDefaultValueExpression()) { + s << ""; + } + if (arg->defaultValueExpression() != 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 << arg->defaultValueExpression() << "
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 (!arg->defaultValueExpression().isEmpty()) + return true; + } + return false; +} + +AbstractMetaArgumentList OverloadData::getArgumentsWithDefaultValues(const AbstractMetaFunction* func) +{ + AbstractMetaArgumentList args; + foreach (AbstractMetaArgument* arg, func->arguments()) { + if (arg->defaultValueExpression().isEmpty() + || func->argumentRemoved(arg->argumentIndex() + 1)) + continue; + args << arg; + } + return args; +} + diff --git a/generator/overloaddata.h b/generator/overloaddata.h new file mode 100644 index 000000000..a0bd4640c --- /dev/null +++ b/generator/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.cpp b/generator/shiboken.cpp new file mode 100644 index 000000000..005df90e2 --- /dev/null +++ b/generator/shiboken.cpp @@ -0,0 +1,27 @@ +/* + * 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 "cppgenerator.h" +#include "headergenerator.h" + +EXPORT_GENERATOR_PLUGIN(new CppGenerator << new HeaderGenerator) diff --git a/generator/shibokengenerator.cpp b/generator/shibokengenerator.cpp new file mode 100644 index 000000000..e7d0b7de7 --- /dev/null +++ b/generator/shibokengenerator.cpp @@ -0,0 +1,1651 @@ +/* + * 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 "shibokengenerator.h" +#include "overloaddata.h" +#include +#include + +#include +#include +#include + +#define NULL_VALUE "NULL" +#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" + +//static void dumpFunction(AbstractMetaFunctionList lst); +static QString baseConversionString(QString typeName); + +QHash ShibokenGenerator::m_pythonPrimitiveTypeName = QHash(); +QHash ShibokenGenerator::m_pythonOperators = QHash(); +QHash ShibokenGenerator::m_formatUnits = QHash(); +QHash ShibokenGenerator::m_tpFuncs = QHash(); + + +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(); +} + +void ShibokenGenerator::clearTpFuncs() +{ + m_tpFuncs["__str__"] = QString("0"); + m_tpFuncs["__repr__"] = 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["unsigned short"] = "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(); + + // 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*="] = "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"); +} + +QString ShibokenGenerator::translateTypeForWrapperMethod(const AbstractMetaType* cType, + const AbstractMetaClass* context, + Options options) const +{ + QString result; + + if (cType->isArray()) { + result = translateTypeForWrapperMethod(cType->arrayElementType(), context, options) + "[]"; + } else { +#ifdef AVOID_PROTECTED_HACK + if (cType->isEnum()) { + const AbstractMetaEnum* metaEnum = findAbstractMetaEnum(cType); + if (metaEnum && metaEnum->isProtected()) + result = protectedEnumSurrogateName(metaEnum); + } + if (result.isEmpty()) +#endif + result = translateType(cType, context, options); + } + + return result; +} + +bool ShibokenGenerator::shouldGenerateCppWrapper(const AbstractMetaClass* metaClass) +{ + bool result = metaClass->isPolymorphic() || metaClass->hasVirtualDestructor(); +#ifdef AVOID_PROTECTED_HACK + 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(); +#endif + return result && !metaClass->isNamespace(); +} + +QString ShibokenGenerator::wrapperName(const AbstractMetaClass* metaClass) +{ + 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) +{ + if (arg->defaultValueExpression().isEmpty()) + return QString(); + + static QRegExp enumValueRegEx("^([A-Za-z_]\\w*)?$"); + QString value = arg->defaultValueExpression(); + QString prefix; + QString suffix; + + if (arg->type()->isEnum()) { + const AbstractMetaEnum* metaEnum = findAbstractMetaEnum(arg->type()); + 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 (type->isValue() || type->isObject()) + return baseConversionString(type->qualifiedCppName() + '*') + "toCpp(" + argName + ')'; + return QString(); +} + +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()); + + //TODO: check these lines + //QString modifiedReturnType = QString(func->typeReplaced(0)); + //return modifiedReturnType.isNull() ? + //translateType(func->type(), func->implementingClass()) : modifiedReturnType; +} + +static QString baseConversionString(QString typeName) +{ + return QString("Shiboken::Converter<%1 >::").arg(typeName); +} + +void ShibokenGenerator::writeBaseConversion(QTextStream& s, const TypeEntry* type) +{ + QString typeName = type->name(); + if (type->isObject()) + typeName.append('*'); +#ifdef AVOID_PROTECTED_HACK + if (type->isEnum()) { + const AbstractMetaEnum* metaEnum = findAbstractMetaEnum(type); + if (metaEnum && metaEnum->isProtected()) + typeName = protectedEnumSurrogateName(metaEnum); + } +#endif + s << baseConversionString(typeName); +} + +void ShibokenGenerator::writeBaseConversion(QTextStream& s, const AbstractMetaType* type, + const AbstractMetaClass* context, Options options) +{ + QString typeName; + if (type->isPrimitive()) { + const PrimitiveTypeEntry* ptype = (const PrimitiveTypeEntry*) type->typeEntry(); + if (ptype->basicAliasedTypeEntry()) + ptype = ptype->basicAliasedTypeEntry(); + typeName = ptype->name(); + } else { + if (type->isObject() || (type->isValue() && !type->isReference())) + options |= Generator::ExcludeConst; + if (type->isContainer() ) + options |= Generator::ExcludeReference | Generator::ExcludeConst; + typeName = translateTypeForWrapperMethod(type, context, options); + } + + s << baseConversionString(typeName); +} + +void ShibokenGenerator::writeToPythonConversion(QTextStream& s, const AbstractMetaType* type, + const AbstractMetaClass* context, const QString& argumentName) +{ + if (!type) + return; + + writeBaseConversion(s, type, context); + s << "toPython"; + + if (!argumentName.isEmpty()) + s << '(' << argumentName << ')'; +} + +void ShibokenGenerator::writeToCppConversion(QTextStream& s, const AbstractMetaClass* metaClass, + const QString& argumentName) +{ + writeBaseConversion(s, metaClass->typeEntry()); + s << "toCpp(" << argumentName << ')'; +} + +void ShibokenGenerator::writeToCppConversion(QTextStream& s, const AbstractMetaType* type, + const AbstractMetaClass* context, const QString& argumentName, + Options options) +{ + writeBaseConversion(s, type, context, options); + s << "toCpp(" << argumentName << ')'; +} + +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 ((type->isObject() || type->isValue() || type->isNamespace())) { // && !type->isReference()) { + baseName = QString("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::cpythonOperatorFunctionName(const AbstractMetaFunction* func) +{ + if (!func->isOperatorOverload()) + return QString(); + return QString("Sbk") + func->ownerClass()->name() + + '_' + pythonOperatorFunctionName(func->originalName()); +} + +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::isCString(const AbstractMetaType* type) +{ + return type->isNativePointer() && type->name() == "char"; +} + +bool ShibokenGenerator::isPairContainer(const AbstractMetaType* type) +{ + return type->isContainer() + && ((ContainerTypeEntry*)type->typeEntry())->type() == ContainerTypeEntry::PairContainer; +} + +bool ShibokenGenerator::shouldDereferenceArgumentPointer(const AbstractMetaArgument* arg) +{ + return shouldDereferenceAbstractMetaTypePointer(arg->type()); +} + +bool ShibokenGenerator::shouldDereferenceAbstractMetaTypePointer(const AbstractMetaType* metaType) +{ + const TypeEntry* type = metaType->typeEntry(); + bool isPointer = metaType->indirections() > 0; + bool isCppClass = type->isValue() || type->isObject(); + return isCppClass && !isPointer && (metaType->isValue() || metaType->isReference()); +} + +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 baseName = cpythonBaseName(metaType); + if (metaType->typeEntry()->isCustom()) { + return guessCPythonCheckFunction(metaType->typeEntry()->name()); + } else if (isNumber(baseName)) { + return genericNumberType ? "SbkNumber_Check" : baseName+"_Check"; + } else { + QString str; + QTextStream s(&str); + writeBaseConversion(s, metaType, 0); + s.flush(); + return str + "checkType"; + } +} + +QString ShibokenGenerator::cpythonCheckFunction(const TypeEntry* type, bool genericNumberType) +{ + QString baseName = cpythonBaseName(type); + if (type->isCustom()) { + return guessCPythonCheckFunction(type->name()); + } else if (isNumber(baseName)) { + return genericNumberType ? "SbkNumber_Check" : baseName+"_Check"; + } else { + QString str; + QTextStream s(&str); + writeBaseConversion(s, type); + s.flush(); + return str + "checkType"; + } +} + +QString ShibokenGenerator::guessCPythonCheckFunction(const QString& type) +{ + QString retval; + AbstractMetaType* metaType = buildAbstractMetaTypeFromString(type); + if (metaType && (metaType->name() != type)) { + retval = cpythonCheckFunction(metaType); + delete metaType; + metaType = 0; + } else if (type == "PyTypeObject") { + retval = "PyType_Check"; + } else { + retval = QString("%1_Check").arg(type); + } + if(metaType) + delete metaType; + return retval; +} + +QString ShibokenGenerator::guessCPythonIsConvertible(const QString& type) +{ + QString retval; + AbstractMetaType* metaType = buildAbstractMetaTypeFromString(type); + if (metaType) { + retval = cpythonIsConvertibleFunction(metaType); + delete metaType; + } else if (type == "PyTypeObject") { + retval = "PyType_Check"; + } else { + retval = QString("%1_Check").arg(type); + } + return retval; +} + +QString ShibokenGenerator::cpythonIsConvertibleFunction(const TypeEntry* type, bool genericNumberType, bool checkExact) +{ + if (checkExact) + return cpythonCheckFunction(type, genericNumberType); + if (type->isCustom()) + return guessCPythonIsConvertible(type->name()); + QString baseName; + QTextStream s(&baseName); + writeBaseConversion(s, type); + s << "isConvertible"; + s.flush(); + return baseName; +} + +QString ShibokenGenerator::cpythonIsConvertibleFunction(const AbstractMetaType* metaType, bool genericNumberType) +{ + + QString baseName = cpythonBaseName(metaType); + if (metaType->typeEntry()->isCustom()) { + return guessCPythonCheckFunction(metaType->typeEntry()->name()); + } else if (isNumber(baseName)) { + return genericNumberType ? "SbkNumber_Check" : baseName+"_Check"; + } else { + QString str; + QTextStream s(&str); + writeBaseConversion(s, metaType, 0); + s.flush(); + return str + "isConvertible"; + } + return baseName; +} + +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 << " self"; + } + + int argUsed = 0; + for (int i = 0; i < arguments.size(); ++i) { + if ((options & Generator::SkipRemovedArguments) && func->argumentRemoved(i+1)) + continue; + + if ((options & Generator::WriteSelf) || argUsed != 0) + s << ", "; + writeArgument(s, func, arguments[i], options); + argUsed++; + } +} + +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; + + if (argCount > 0) + s << ", "; + s << 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()) + ) + s << "_out"; + + 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 << ')'; +} + +AbstractMetaFunctionList ShibokenGenerator::filterFunctions(const AbstractMetaClass* metaClass) +{ + AbstractMetaFunctionList result; + foreach (AbstractMetaFunction *func, metaClass->functions()) { + //skip signals + if (func->isSignal() + || func->isDestructor() +#ifndef AVOID_PROTECTED_HACK + || (func->isModifiedRemoved() && !func->isAbstract())) { +#else + || (func->isModifiedRemoved() && !func->isAbstract() && !func->isProtected())) { +#endif + 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; +} + +void ShibokenGenerator::writeCodeSnips(QTextStream& s, + const CodeSnipList& codeSnips, + CodeSnip::Position position, + TypeSystem::Language language, + const AbstractMetaFunction* func, + const AbstractMetaArgument* lastArg, + const AbstractMetaClass* context) +{ + static QRegExp toPythonRegex("%CONVERTTOPYTHON\\[([^\\[]*)\\]"); + static QRegExp toCppRegex("%CONVERTTOCPP\\[([^\\[]*)\\]"); + static QRegExp pyArgsRegex("%PYARG_(\\d+)"); + + // detect is we should use pyargs instead of args as variable name for python arguments + bool usePyArgs = false; + int numArgs = 0; + if (func) { + // calc num of real arguments. + int argsRemoved = 0; + for (int i = 0; i < func->arguments().size(); i++) { + if (func->argumentRemoved(i+1)) + argsRemoved++; + } + numArgs = func->arguments().size() - argsRemoved; + usePyArgs = pythonFunctionWrapperUsesListOfArguments(OverloadData(getFunctionGroups(func->implementingClass())[func->name()], this)); + } + + foreach (CodeSnip snip, codeSnips) { + if ((position != CodeSnip::Any && snip.position != position) || !(snip.language & language)) + continue; + + QString code; + QTextStream tmpStream(&code); + formatCode(tmpStream, snip.code(), INDENT); + + if (context) { + // replace template variable for the Python Type object for the + // class context in which the variable is used + code.replace("%PYTHONTYPEOBJECT", cpythonTypeName(context) + ".super.ht_type"); + } + + // replace "toPython "converters + code.replace(toPythonRegex, "Shiboken::Converter<\\1 >::toPython"); + + // replace "toCpp "converters + code.replace(toCppRegex, "Shiboken::Converter<\\1 >::toCpp"); + + if (func) { + // replace %PYARG_# variables + code.replace("%PYARG_0", PYTHON_RETURN_VAR); + if (snip.language == TypeSystem::TargetLangCode) { + if (usePyArgs) { + code.replace(pyArgsRegex, "pyargs[\\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()); + else + code.replace("%PYARG_1", usePyArgs ? "pyargs[0]" : "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(pyargs, \\1-1, \\2)"); + + code.replace(pyArgsRegex, "PyTuple_GET_ITEM(pyargs, \\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); + } + + static QRegExp cppArgTypeRegexCheck("%ARG(\\d+)_TYPE"); + int pos = 0; + 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 = func->type()->isObject() || func->type()->isQObject() ? "%1->" : "%1."; + if (func->type()->typeEntry()->isValue() || func->type()->typeEntry()->isObject()) + code.replace("%0.", returnValueOp.arg(CPP_RETURN_VAR)); + code.replace("%0", CPP_RETURN_VAR); + } + + // replace template variable for self Python object + QString pySelf; + if (snip.language == TypeSystem::NativeCode) + pySelf = "pySelf"; + else + pySelf = "self"; + code.replace("%PYSELF", pySelf); + + // replace template variable for pointer to C++ this object + if (func->implementingClass()) { + QString cppSelf; + QString replacement("%1->"); + if (func->isStatic()) { + cppSelf = func->ownerClass()->qualifiedCppName(); + replacement = "%1::"; + } else if (snip.language == TypeSystem::NativeCode) { + cppSelf = "this"; + } else { + cppSelf = "cppSelf"; + } + code.replace("%CPPSELF.", replacement.arg(cppSelf)); + code.replace("%CPPSELF", cppSelf); + + // 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)); + } + } + + // replace template variables %# for individual arguments + int removed = 0; + for (int i = 0; i < func->arguments().size(); i++) { + const AbstractMetaArgument* arg = func->arguments().at(i); + QString argReplacement; + if (snip.language == TypeSystem::TargetLangCode) { + if (!lastArg || func->argumentRemoved(i+1)) { + if (!arg->defaultValueExpression().isEmpty()) + argReplacement = arg->defaultValueExpression(); + removed++; + } else if (lastArg && (arg->argumentIndex() > lastArg->argumentIndex())) { + argReplacement = arg->defaultValueExpression(); + } + + if (argReplacement.isEmpty()) { + if (arg->type()->typeEntry()->isCustom()) { + argReplacement = usePyArgs ? QString("pyargs[%1]").arg(i - removed) : "arg"; + } else { + argReplacement = QString("cpp_arg%1").arg(i - removed); + if (shouldDereferenceArgumentPointer(arg)) + argReplacement.prepend("(*").append(')'); + } + } + } else { + argReplacement = arg->name(); + } + code.replace("%" + QString::number(i+1), argReplacement); + } + + // replace template %ARGUMENT_NAMES variable for a list of arguments + removed = 0; + QStringList argumentNames; + foreach (const AbstractMetaArgument* arg, func->arguments()) { + if (snip.language == TypeSystem::TargetLangCode) { + if (func->argumentRemoved(arg->argumentIndex() + 1)) { + if (!arg->defaultValueExpression().isEmpty()) + argumentNames << arg->defaultValueExpression(); + removed++; + continue; + } + + QString argName; + if (lastArg && arg->argumentIndex() > lastArg->argumentIndex()) { + argName = arg->defaultValueExpression(); + } else { + argName = QString("cpp_arg%1").arg(arg->argumentIndex() - removed); + if (shouldDereferenceArgumentPointer(arg)) + argName.prepend('*'); + } + argumentNames << argName; + } else { + argumentNames << arg->name(); + } + } + code.replace("%ARGUMENT_NAMES", argumentNames.join(", ")); + + if (snip.language == TypeSystem::NativeCode) { + // replace template %PYTHON_ARGUMENTS variable for 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", "pyargs"); + + // 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", "py_override"); + } + +#ifdef AVOID_PROTECTED_HACK + // 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())); + } +#endif + + if (func->isConstructor() && shouldGenerateCppWrapper(func->ownerClass())) + code.replace("%TYPE", wrapperName(func->ownerClass())); + + replaceTemplateVariables(code, func); + } + + if (!code.isEmpty()) { + s << INDENT << "// Begin code injection" << endl; + s << code; + s << INDENT << "// End of code injection" << endl; + } + } +} + +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::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) +{ + AbstractMetaClassList baseClasses; + 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) +{ + AbstractMetaClassList result; + 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() || metaClass->typeEntry()->isObject()) + 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 typeString) +{ + typeString = typeString.trimmed(); + 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(); + } + + TypeEntry* typeEntry = TypeDatabase::instance()->findType(typeString); + AbstractMetaType* metaType = 0; + if (typeEntry) { + metaType = new AbstractMetaType(); + metaType->setTypeEntry(typeEntry); + metaType->setIndirections(indirections); + metaType->setReference(isReference); + metaType->setConstant(isConst); + if (metaType->name() == "char" && metaType->indirections() == 1) + metaType->setTypeUsagePattern(AbstractMetaType::NativePointerPattern); + } + return metaType; +} + +/* +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(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."); + return opts; +} + +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); + return true; +} + +bool ShibokenGenerator::useCtorHeuristic() const +{ + return m_useCtorHeuristic; +} + +bool ShibokenGenerator::useReturnValueHeuristic() const +{ + return m_userReturnValueHeuristic; +} + +bool ShibokenGenerator::usePySideExtensions() const +{ + return m_usePySideExtensions; +} + +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::getTypeIndexVariableName(const TypeEntry* metaType) +{ + QString res("SBK_"); + res += metaType->qualifiedCppName(); + res.replace("::", "_"); + res.replace("<", "_"); + res.replace(">", "_"); + res += "_IDX"; + return res.toUpper(); +} + +bool ShibokenGenerator::verboseErrorMessagesDisabled() const +{ + return m_verboseErrorMessagesDisabled; +} + + +bool ShibokenGenerator::pythonFunctionWrapperUsesListOfArguments(const OverloadData& overloadData) +{ + int maxArgs = overloadData.maxArgs(); + int minArgs = overloadData.minArgs(); + bool usePyArgs = (minArgs != maxArgs) + || (maxArgs > 1) + || overloadData.referenceFunction()->isConstructor() + || overloadData.hasArgumentWithDefaultValue(); + return usePyArgs; +} + diff --git a/generator/shibokengenerator.h b/generator/shibokengenerator.h new file mode 100644 index 000000000..1a7c5a63f --- /dev/null +++ b/generator/shibokengenerator.h @@ -0,0 +1,364 @@ +/* + * 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 SHIBOKENGENERATOR_H +#define SHIBOKENGENERATOR_H + +#define PYTHON_RETURN_VAR "pyResult" +#define CPP_RETURN_VAR "cppResult" +#define THREAD_STATE_SAVER_VAR "threadStateSaver" +#define CPP_SELF_VAR "cppSelf" + +#include +#include + +#include "overloaddata.h" + +class DocParser; + +/** + * Abstract generator that contains common methods used in CppGenerator and HeaderGenerator. + */ +class ShibokenGenerator : public Generator +{ +public: + 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; + /** + * Write a code snip into the buffer \p s. + * CodeSnip are codes inside inject-code tags. + * \param s the buffer + * \param code_snips a list of code snips + * \param position the position to insert the code snip + * \param language the kind of code snip + * \param func the cpp function + * \param lastArg last argument whose value is available, usually the last; + * a NULL pointer indicates that no argument will be available, + * i.e. a call without arguments. + * \param context the class context for the place where the code snip will be written + */ + void writeCodeSnips(QTextStream &s, + const CodeSnipList &code_snips, + CodeSnip::Position position, + TypeSystem::Language language, + const AbstractMetaFunction* func = 0, + const AbstractMetaArgument* lastArg = 0, + const AbstractMetaClass* context = 0); + + /** + * 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); + + /** + * 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); + + /// Returns a list of all ancestor classes for the given class. + AbstractMetaClassList getAllAncestors(const AbstractMetaClass* metaClass); + + const AbstractMetaClass* getMultipleInheritingClass(const AbstractMetaClass* metaClass); + + void writeBaseConversion(QTextStream& s, const AbstractMetaType* type, + const AbstractMetaClass* context, Options options = NoOption); + /// Simpler version of writeBaseConversion, uses only the base name of the type. + void writeBaseConversion(QTextStream& s, const TypeEntry* type); + void writeToPythonConversion(QTextStream& s, const AbstractMetaType* type, + const AbstractMetaClass* context, const QString& argumentName = QString()); + void writeToCppConversion(QTextStream& s, const AbstractMetaType* type, + const AbstractMetaClass* context, const QString& argumentName, Options options = NoOption); + void writeToCppConversion(QTextStream& s, const AbstractMetaClass* metaClass, const QString& argumentName); + + /// Verifies if the class should have a C++ wrapper generated for it, instead of only a Python wrapper. + static bool shouldGenerateCppWrapper(const AbstractMetaClass* metaClass); + + static QString wrapperName(const AbstractMetaClass* metaClass); + + 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 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 isCString(const AbstractMetaType* type); + static bool isPairContainer(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 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 cpythonCheckFunction(const TypeEntry* type, bool genericNumberType = false); + QString cpythonCheckFunction(const AbstractMetaType* metaType, bool genericNumberType = false); + QString guessCPythonCheckFunction(const QString& type); + QString cpythonIsConvertibleFunction(const TypeEntry* type, bool genericNumberType = false, bool checkExact = false); + QString cpythonIsConvertibleFunction(const AbstractMetaType* metaType, bool genericNumberType = false); + QString cpythonIsConvertibleFunction(const AbstractMetaArgument* metaArg, bool genericNumberType = false) + { + return cpythonIsConvertibleFunction(metaArg->type(), genericNumberType); + } + QString guessCPythonIsConvertible(const QString& 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 = "self"); + 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); + QString cpythonEnumName(const AbstractMetaEnum* metaEnum) + { + return cpythonEnumName(metaEnum->typeEntry()); + } + + QString cpythonFlagsName(const FlagsTypeEntry* flagsEntry); + 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; + QString cppApiVariableName(const QString& moduleName = QString()) const; + QString getTypeIndexVariableName(const TypeEntry* metaType); + /// 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 typeString 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 typeString); + +protected: + bool doSetup(const QMap& args); + // 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; + + void clearTpFuncs(); + + const char* name() const { return "Shiboken"; } + + /** + * Initialize correspondences between primitive and Python types + */ + static void initPrimitiveTypesCorrespondences(); + + void writeFunctionCall(QTextStream& s, + const AbstractMetaFunction* metaFunc, + Options options = NoOption) const; + + static 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 true if the Python wrapper for the received OverloadData must accept a list of arguments. + static bool pythonFunctionWrapperUsesListOfArguments(const OverloadData& overloadData); + + Indentor INDENT; +private: + bool m_useCtorHeuristic; + bool m_userReturnValueHeuristic; + bool m_usePySideExtensions; + bool m_verboseErrorMessagesDisabled; +}; + + +#endif // SHIBOKENGENERATOR_H + diff --git a/headergenerator.cpp b/headergenerator.cpp deleted file mode 100644 index 2e060f429..000000000 --- a/headergenerator.cpp +++ /dev/null @@ -1,610 +0,0 @@ -/* - * 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 - * - */ - -#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 -{ - QString fieldType = field->type()->cppSignature(); - QString fieldName = field->enclosingClass()->qualifiedCppName() + "::" + field->name(); - - s << INDENT << "inline " << fieldType << ' ' << protectedFieldGetterName(field) << "()"; - s << " { return " << fieldName << "; }" << endl; - s << INDENT << "inline void " << protectedFieldSetterName(field) << '(' << fieldType << " value)"; - s << " { " << fieldName << " = value; }" << endl; -} - -void HeaderGenerator::generateClass(QTextStream& s, const AbstractMetaClass* metaClass) -{ - ReportHandler::debugSparse("Generating header for " + metaClass->fullName()); - 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; - -#ifndef AVOID_PROTECTED_HACK - s << "#define protected public" << endl << endl; -#endif - - 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; - - if (metaClass->typeEntry()->isValue()) - writeCopyCtor(s, metaClass); - - bool hasVirtualFunction = false; - foreach (AbstractMetaFunction *func, filterFunctions(metaClass)) { - if (func->isVirtual()) - hasVirtualFunction = true; - writeFunction(s, func); - } - -#ifdef AVOID_PROTECTED_HACK - if (metaClass->hasProtectedFields()) { - foreach (AbstractMetaField* field, metaClass->fields()) { - if (!field->isProtected()) - continue; - writeProtectedFieldAccessors(s, field); - } - } -#endif - - //destructor -#ifdef AVOID_PROTECTED_HACK - if (!metaClass->hasPrivateDestructor()) -#endif - s << INDENT << (metaClass->hasVirtualDestructor() || hasVirtualFunction ? "virtual " : "") << "~" << wrapperName << "();" << endl; - - writeCodeSnips(s, metaClass->typeEntry()->codeSnips(), CodeSnip::Declaration, TypeSystem::NativeCode); - -#ifdef AVOID_PROTECTED_HACK - if (!metaClass->hasPrivateDestructor()) { -#endif - - if (usePySideExtensions() && metaClass->isQObject()) { - s << "public:\n"; - s << INDENT << "virtual int qt_metacall(QMetaObject::Call call, int id, void** args);\n"; - s << "private:\n"; - s << INDENT << "mutable PySide::DynamicQMetaObject* m_metaObject;\n"; - } - -#ifdef AVOID_PROTECTED_HACK - } -#endif - - s << "};" << endl << endl; - } - - s << "#endif // SBK_" << headerGuard << "_H" << endl << endl; -} - -void HeaderGenerator::writeFunction(QTextStream& s, const AbstractMetaFunction* func) const -{ - // do not write copy ctors here. - if (func->isCopyConstructor()) - return; - - if (func->isConstructor() && func->isUserAdded()) - return; - -#ifdef AVOID_PROTECTED_HACK - if (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; - } -#endif - - // pure virtual functions need a default implementation - if ((func->isPrivate() && !visibilityModifiedToPrivate(func)) - || (func->isModifiedRemoved() && !func->isAbstract())) - return; - -#ifdef AVOID_PROTECTED_HACK - if (func->ownerClass()->hasPrivateDestructor() && (func->isAbstract() || func->isVirtual())) - return; -#endif - - 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; - - // TODO: when modified an abstract method ceases to be virtual but stays abstract - //if (func->isModifiedRemoved() && func->isAbstract()) { - //} - } -} - -void HeaderGenerator::writeTypeConverterDecl(QTextStream& s, const TypeEntry* type) -{ - s << "template<>" << endl; - - const AbstractMetaClass* metaClass = classes().findClass(type->name()); - bool isAbstractOrObjectType = (metaClass && metaClass->isAbstract()) || type->isObject(); - - AbstractMetaFunctionList implicitConvs; - foreach (AbstractMetaFunction* func, implicitConversions(type)) { - if (!func->isUserAdded()) - implicitConvs << func; - } - bool isValueTypeWithImplConversions = type->isValue() && !implicitConvs.isEmpty(); - bool hasCustomConversion = type->hasConversionRule(); - QString typeT = type->name() + (isAbstractOrObjectType ? "*" : ""); - QString typeName = type->name(); - -#ifdef AVOID_PROTECTED_HACK - const AbstractMetaEnum* metaEnum = findAbstractMetaEnum(type); - if (metaEnum && metaEnum->isProtected()) { - typeT = protectedEnumSurrogateName(metaEnum); - typeName = typeT; - } -#endif - - s << "struct Converter<" << typeT << " >"; - if (!hasCustomConversion) { - if (type->isEnum()) - s << " : EnumConverter"; - else if (type->isFlags()) - s << " : QFlagsConverter"; - else if (isAbstractOrObjectType) - s << " : ObjectTypeConverter"; - else - s << " : ValueTypeConverter"; - s << '<' << typeName << " >"; - } - s << endl << '{' << endl; - if (isValueTypeWithImplConversions || hasCustomConversion) { - s << INDENT << "static " << type->name() << " toCpp(PyObject* pyobj);" << endl; - s << INDENT << "static bool isConvertible(PyObject* pyobj);" << endl; - if (hasCustomConversion) { - s << INDENT << "static bool checkType(PyObject* pyobj);" << endl; - s << INDENT << "static inline PyObject* toPython(void* cppObj) { return toPython(*reinterpret_cast<" - << type->name() << (isAbstractOrObjectType ? "" : "*") << " >(cppObj)); }" << endl; - s << INDENT << "static PyObject* toPython(const " << type->name() << "& cppObj);" << endl; - } - } - s << "};" << endl; - - // write value-type like converter to object-types - if (isAbstractOrObjectType) { - s << endl << "template<>" << endl; - s << "struct Converter<" << type->name() << "& > : ObjectTypeReferenceConverter<" << type->name() << " >" << endl << '{' << endl; - s << "};" << endl; - } -} - -void HeaderGenerator::writeTypeIndexDefineLine(QTextStream& s, const TypeEntry* typeEntry, int& idx) -{ - if (!typeEntry || !typeEntry->generateCode()) - return; - s.setFieldAlignment(QTextStream::AlignLeft); - s << "#define "; - s.setFieldWidth(60); - s << getTypeIndexVariableName(typeEntry); - s.setFieldWidth(0); - s << ' ' << (idx++) << endl; - if (typeEntry->isEnum()) { - const EnumTypeEntry* ete = reinterpret_cast(typeEntry); - if (ete->flags()) - writeTypeIndexDefineLine(s, ete->flags(), idx); - } -} - -void HeaderGenerator::writeTypeIndexDefine(QTextStream& s, const AbstractMetaClass* metaClass, int& idx) -{ - if (!metaClass->typeEntry()->generateCode()) - return; - writeTypeIndexDefineLine(s, metaClass->typeEntry(), idx); - foreach (const AbstractMetaEnum* metaEnum, metaClass->enums()) - writeTypeIndexDefineLine(s, metaEnum->typeEntry(), idx); -} - -void HeaderGenerator::finishGeneration() -{ - if (classes().isEmpty()) - return; - - // 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 convertersDecl; - QTextStream convDecl(&convertersDecl); - QString sbkTypeFunctions; - QTextStream typeFunctions(&sbkTypeFunctions); - QString converterImpl; - QTextStream convImpl(&converterImpl); - - Indentation indent(INDENT); - - macrosStream << "// Type indices" << endl; - int idx = 0; - foreach (const AbstractMetaClass* metaClass, classes()) - writeTypeIndexDefine(macrosStream, metaClass, idx); - foreach (const AbstractMetaEnum* metaEnum, globalEnums()) - writeTypeIndexDefineLine(macrosStream, metaEnum->typeEntry(), idx); - macrosStream << "#define "; - macrosStream.setFieldWidth(60); - macrosStream << "SBK_"+moduleName()+"_IDX_COUNT"; - macrosStream.setFieldWidth(0); - macrosStream << ' ' << idx << endl << endl; - macrosStream << "// This variable stores all python types exported by this module" << endl; - macrosStream << "extern PyTypeObject** " << cppApiVariableName() << ';' << endl << endl; - - macrosStream << "// Macros for type check" << endl; - foreach (const AbstractMetaEnum* cppEnum, globalEnums()) { - if (cppEnum->isAnonymous()) - continue; - includes << cppEnum->typeEntry()->include(); - writeTypeConverterDecl(convDecl, cppEnum->typeEntry()); - convDecl << endl; - 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()) - continue; - EnumTypeEntry* enumType = cppEnum->typeEntry(); - includes << enumType->include(); - writeTypeConverterDecl(convDecl, enumType); - FlagsTypeEntry* flagsEntry = enumType->flags(); - if (flagsEntry) - writeTypeConverterDecl(convDecl, flagsEntry); - convDecl << endl; - writeSbkTypeFunction(typeFunctions, cppEnum); - } - - if (!metaClass->isNamespace()) { - writeSbkTypeFunction(typeFunctions, metaClass); - writeSbkCopyCppObjectFunction(convDecl, metaClass); - writeTypeConverterDecl(convDecl, classType); - writeTypeConverterImpl(convImpl, classType); - convDecl << endl; - } - } - - 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; - #ifndef AVOID_PROTECTED_HACK - s << "//workaround to access protected functions" << endl; - s << "#define protected public" << endl << endl; - #endif - - 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; - - s << "namespace Shiboken" << endl << '{' << endl << endl; - - s << "// PyType functions, to get the PyObjectType for a type T\n"; - s << sbkTypeFunctions << endl; - - if (usePySideExtensions()) { - foreach (AbstractMetaClass* metaClass, classes()) { - if (!metaClass->isQObject() || !metaClass->typeEntry()->generateCode()) - continue; - - s << "template<>" << endl; - s << "inline PyObject* createWrapper<" << metaClass->qualifiedCppName() << " >(const "; - s << metaClass->qualifiedCppName() << "* cppobj, bool hasOwnership, bool isExactType)" << endl; - s << '{' << endl; - s << INDENT << "PyObject* pyObj = Shiboken::SbkBaseWrapper_New(reinterpret_cast(SbkType<" << metaClass->qualifiedCppName() << " >())," - << "const_cast<" << metaClass->qualifiedCppName() << "*>(cppobj), hasOwnership, isExactType);" << endl; - s << INDENT << "PySide::signalUpdateSource(pyObj);" << endl; - s << INDENT << "return pyObj;" << endl; - s << '}' << endl; - - } - } - - s << "// Generated converters declarations ----------------------------------" << endl << endl; - s << convertersDecl; - s << "} // namespace Shiboken" << endl << endl; - - s << "// User defined converters --------------------------------------------" << endl; - foreach (TypeEntry* typeEntry, TypeDatabase::instance()->entries()) { - if (typeEntry->hasConversionRule()) { - s << "// Conversion rule for: " << typeEntry->name() << endl; - s << typeEntry->conversionRule(); - } - } - s << "// Generated converters implemantations -------------------------------" << endl << endl; - s << converterImpl << endl; - - s << "#endif // " << includeShield << endl << endl; -} - -void HeaderGenerator::writeSbkTypeFunction(QTextStream& s, const AbstractMetaEnum* cppEnum) -{ - QString enumName = cppEnum->name(); - if (cppEnum->enclosingClass()) - enumName = cppEnum->enclosingClass()->qualifiedCppName() + "::" + enumName; -#ifdef AVOID_PROTECTED_HACK - if (cppEnum->isProtected()) { - enumName = protectedEnumSurrogateName(cppEnum); - s << "enum " << enumName << " {};" << endl; - } -#endif - - 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::writeSbkCopyCppObjectFunction(QTextStream& s, const AbstractMetaClass* metaClass) -{ - if (!metaClass->typeEntry()->isValue() || !shouldGenerateCppWrapper(metaClass)) - return; - QString className = metaClass->qualifiedCppName(); - s << "template <>" << endl; - s << "struct SbkTypeInfo<" << className << " >" << endl; - s << '{' << endl; - s << INDENT << "static const bool isCppWrapper = true;" << endl; - s << "};" << endl; -} - -void HeaderGenerator::writeTypeConverterImpl(QTextStream& s, const TypeEntry* type) -{ - if (type->hasConversionRule()) - return; - - QString pyTypeName = cpythonTypeName(type); - - AbstractMetaFunctionList implicitConvs; - foreach (AbstractMetaFunction* func, implicitConversions(type)) { - if (!func->isUserAdded()) - implicitConvs << func; - } - - bool hasImplicitConversions = !implicitConvs.isEmpty(); - - // A specialized Converter::toCpp method is only need for - // classes with implicit conversions. - if (!hasImplicitConversions) - return; - - // Write Converter::isConvertible - s << "inline bool Shiboken::Converter<" << type->name() << " >::isConvertible(PyObject* pyobj)" << endl; - s << '{' << endl; - - if (type->isValue()) { - s << INDENT << "if (ValueTypeConverter<" << type->name() << " >::isConvertible(pyobj))" << endl; - Indentation indent(INDENT); - s << INDENT << "return true;" << endl; - } - - - s << INDENT << "SbkBaseWrapperType* shiboType = reinterpret_cast(SbkType<"; - s << type->name() << " >());" << endl; - s << INDENT << "return "; - bool isFirst = true; - foreach (const AbstractMetaFunction* ctor, implicitConvs) { - Indentation indent(INDENT); - if (isFirst) - isFirst = false; - else - s << endl << INDENT << " || "; - if (ctor->isConversionOperator()) - s << cpythonCheckFunction(ctor->ownerClass()->typeEntry()); - else - s << cpythonCheckFunction(ctor->arguments().first()->type()); - s << "(pyobj)"; - } - s << endl; - { - Indentation indent(INDENT); - s << INDENT << " || (shiboType->ext_isconvertible && shiboType->ext_isconvertible(pyobj));" << endl; - } - s << '}' << endl << endl; - - // Write Converter::toCpp function - s << "inline " << type->name() << " Shiboken::Converter<" << type->name() << " >::toCpp(PyObject* pyobj)" << endl; - s << '{' << endl; - - s << INDENT << "if (!Shiboken_TypeCheck(pyobj, " << type->name() << ")) {" << endl; - { - Indentation indent(INDENT); - s << INDENT << "SbkBaseWrapperType* shiboType = reinterpret_cast(SbkType<"; - s << type->name() << " >());" << endl; - } - bool firstImplicitIf = true; - foreach (const AbstractMetaFunction* ctor, implicitConvs) { - if (ctor->isModifiedRemoved()) - continue; - - Indentation indent(INDENT); - s << INDENT; - if (firstImplicitIf) - firstImplicitIf = false; - else - s << "else "; - - QString typeCheck; - QString toCppConv; - QTextStream tcc(&toCppConv); - if (ctor->isConversionOperator()) { - const AbstractMetaClass* metaClass = ctor->ownerClass(); - typeCheck = cpythonCheckFunction(metaClass->typeEntry()); - writeToCppConversion(tcc, metaClass, "pyobj"); - } else { - const AbstractMetaType* argType = ctor->arguments().first()->type(); - typeCheck = cpythonCheckFunction(argType); - writeToCppConversion(tcc, argType, 0, "pyobj"); - } - - s << "if (" << typeCheck << "(pyobj))" << endl; - { - Indentation indent(INDENT); - s << INDENT << "return " << type->name() << '(' << toCppConv << ");" << endl; - } - } - - { - Indentation indent(INDENT); - s << INDENT << "else if (shiboType->ext_isconvertible && shiboType->ext_tocpp && shiboType->ext_isconvertible(pyobj)) {" << endl; - { - Indentation indent(INDENT); - s << INDENT << type->name() << "* cptr = reinterpret_cast<" << type->name() << "*>(shiboType->ext_tocpp(pyobj));" << endl; - s << INDENT << "std::auto_ptr<" << type->name() << " > cptr_auto_ptr(cptr);" << endl; - s << INDENT << "return *cptr;" << endl; - } - s << INDENT << '}' << endl; - } - - s << INDENT << '}' << endl; - - s << INDENT << "return *" << cpythonWrapperCPtr(type, "pyobj") << ';' << endl; - s << '}' << endl << endl; -} - diff --git a/headergenerator.h b/headergenerator.h deleted file mode 100644 index 9802a104f..000000000 --- a/headergenerator.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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) const; - void writePureVirtualEmptyImpl(QTextStream& , const AbstractMetaFunction* func) const; - void writeDefaultImplementation(QTextStream& s, const AbstractMetaFunction* func) const; - void writeTypeConverterDecl(QTextStream& s, const TypeEntry* type); - void writeSbkTypeFunction(QTextStream& s, const AbstractMetaEnum* cppEnum); - void writeSbkTypeFunction(QTextStream& s, const AbstractMetaClass* cppClass); - void writeSbkCopyCppObjectFunction(QTextStream& s, const AbstractMetaClass* metaClass); - void writeTypeIndexDefineLine(QTextStream& s, const TypeEntry* typeEntry, int& idx); - void writeTypeIndexDefine(QTextStream& s, const AbstractMetaClass* metaClass, int& idx); - void writeTypeConverterImpl(QTextStream& s, const TypeEntry* type); - -}; - -#endif // HEADERGENERATOR_H - diff --git a/libshiboken/CMakeLists.txt b/libshiboken/CMakeLists.txt index bfd160b10..f21681f55 100644 --- a/libshiboken/CMakeLists.txt +++ b/libshiboken/CMakeLists.txt @@ -1,4 +1,12 @@ project(libshiboken) + +option(ENABLE_VERSION_SUFFIX "Used to use current version in suffix to generated files. This is used to allow multiples versions installed simultaneous." FALSE) +if(ENABLE_VERSION_SUFFIX) + set(shiboken_SUFFIX "-${shiboken_MAJOR_VERSION}.${shiboken_MINOR_VERSION}") +else() + set(shiboken_SUFFIX "") +endif() + #Find installed sparsehash find_path(SPARSEHASH_INCLUDE_PATH sparseconfig.h PATH_SUFFIXES "/google/sparsehash") if(SPARSEHASH_INCLUDE_PATH) diff --git a/main.cpp b/main.cpp deleted file mode 100644 index 051225061..000000000 --- a/main.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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 - -int main(int argc, char *argv[]) -{ - QStringList args; - args.append("--generatorSet=shiboken"); - for (int i = 1; i < argc; i++) - args.append(argv[i]); - return QProcess::execute("generatorrunner", args); -} - diff --git a/overloaddata.cpp b/overloaddata.cpp deleted file mode 100644 index a71091ea3..000000000 --- a/overloaddata.cpp +++ /dev/null @@ -1,788 +0,0 @@ -/* - * 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" - -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; -} - -/** - * 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() -{ - QHash map; // type_name -> id - QHash reverseMap; // id -> type_name - bool checkPyObject = false; - int pyobjectIndex = 0; - - // sort the children overloads - foreach(OverloadData *ov, m_nextOverloadData) { - ov->sortNextOverloads(); - } - - if (m_nextOverloadData.size() <= 1) - return; - - // Creates the 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. - int i = 0; - foreach(OverloadData* ov, m_nextOverloadData) { - const TypeEntry* typeEntry = getAliasedTypeEntry(ov->argType()->typeEntry()); - map[typeEntry->name()] = i; - reverseMap[i] = ov; - - if (!checkPyObject && typeEntry->name().contains("PyObject")) { - checkPyObject = true; - pyobjectIndex = i; - } - i++; - } - - // Create the graph of type dependencies based on implicit conversions. - Graph graph(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 int numPrimitives = sizeof(primitiveTypes)/sizeof(const char*); - bool hasPrimitive[numPrimitives]; - for (int i = 0; i < numPrimitives; ++i) - hasPrimitive[i] = map.contains(primitiveTypes[i]); - // just some alias - bool haveInt = hasPrimitive[0]; - bool haveLong = hasPrimitive[2]; - bool haveShort = hasPrimitive[4]; - - foreach(OverloadData* ov, m_nextOverloadData) { - const AbstractMetaType* targetType = ov->argType(); - const TypeEntry* targetTypeEntry = getAliasedTypeEntry(targetType->typeEntry()); - - foreach(AbstractMetaFunction* function, m_generator->implicitConversions(ov->argType())) { - QString convertibleType; - if (function->isConversionOperator()) - convertibleType = function->ownerClass()->typeEntry()->name(); - else - convertibleType = function->arguments().first()->type()->typeEntry()->name(); - - if (!map.contains(convertibleType)) - continue; - - int targetTypeId = map[targetTypeEntry->name()]; - int convertibleTypeId = 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); - } - - if (targetType->hasInstantiations()) { - foreach(const AbstractMetaType *instantiation, targetType->instantiations()) { - if (map.contains(instantiation->typeEntry()->name())) { - int target = map[targetTypeEntry->name()]; - int convertible = map[instantiation->typeEntry()->name()]; - - if (!graph.containsEdge(target, convertible)) // Avoid cyclic dependency. - graph.addEdge(convertible, target); - } - } - } - - /* Add dependency on PyObject, so its check is the last one (too generic) */ - if (checkPyObject && !targetTypeEntry->name().contains("PyObject")) { - graph.addEdge(map[targetTypeEntry->name()], pyobjectIndex); - } - - if (targetTypeEntry->isEnum()) { - for (int i = 0; i < numPrimitives; ++i) { - if (hasPrimitive[i]) - graph.addEdge(map[targetTypeEntry->name()], map[primitiveTypes[i]]); - } - } - } - - // Special case for double(int i) (not tracked by m_generator->implicitConversions - if (haveInt) { - if (map.contains("float")) - graph.addEdge(map["float"], map["int"]); - if (map.contains("double")) - graph.addEdge(map["double"], map["int"]); - if (map.contains("bool")) - graph.addEdge(map["bool"], map["int"]); - } - - if (haveShort) { - if (map.contains("float")) - graph.addEdge(map["float"], map["short"]); - if (map.contains("double")) - graph.addEdge(map["double"], map["short"]); - if (map.contains("bool")) - graph.addEdge(map["bool"], map["short"]); - } - - if (haveLong) { - if (map.contains("float")) - graph.addEdge(map["float"], map["long"]); - if (map.contains("double")) - graph.addEdge(map["double"], map["long"]); - if (map.contains("bool")) - graph.addEdge(map["bool"], map["long"]); - } - - // sort the overloads topologicaly based on the deps graph. - - QLinkedList unmappedResult = graph.topologicalSort(); - if (unmappedResult.isEmpty()) { - QString funcName = referenceFunction()->name(); - if (referenceFunction()->ownerClass()) - funcName.prepend(referenceFunction()->ownerClass()->name() + '.'); - ReportHandler::warning(QString("Cyclic dependency found on overloaddata for '%1' method!").arg(qPrintable(funcName))); - } - - m_nextOverloadData.clear(); - foreach(int i, unmappedResult) - m_nextOverloadData << 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 (!func->arguments()[i]->defaultValueExpression().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 && tmp->m_argType->typeEntry() == argType->typeEntry()) - || (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 (!func->arguments()[m_argPos + removedArgs]->defaultValueExpression().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 (!args[i]->defaultValueExpression().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 && !func->arguments()[j]->defaultValueExpression().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; - if (!arg->defaultValueExpression().isEmpty() || - arg->defaultValueExpression() != arg->originalDefaultValueExpression()) { - s << ""; - } - if (arg->defaultValueExpression() != 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 << arg->defaultValueExpression() << "
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 (!arg->defaultValueExpression().isEmpty()) - return true; - } - return false; -} - -AbstractMetaArgumentList OverloadData::getArgumentsWithDefaultValues(const AbstractMetaFunction* func) -{ - AbstractMetaArgumentList args; - foreach (AbstractMetaArgument* arg, func->arguments()) { - if (arg->defaultValueExpression().isEmpty() - || func->argumentRemoved(arg->argumentIndex() + 1)) - continue; - args << arg; - } - return args; -} - diff --git a/overloaddata.h b/overloaddata.h deleted file mode 100644 index a0bd4640c..000000000 --- a/overloaddata.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - * 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/shiboken.cpp b/shiboken.cpp deleted file mode 100644 index 005df90e2..000000000 --- a/shiboken.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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 "cppgenerator.h" -#include "headergenerator.h" - -EXPORT_GENERATOR_PLUGIN(new CppGenerator << new HeaderGenerator) diff --git a/shibokengenerator.cpp b/shibokengenerator.cpp deleted file mode 100644 index e7d0b7de7..000000000 --- a/shibokengenerator.cpp +++ /dev/null @@ -1,1651 +0,0 @@ -/* - * 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 "shibokengenerator.h" -#include "overloaddata.h" -#include -#include - -#include -#include -#include - -#define NULL_VALUE "NULL" -#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" - -//static void dumpFunction(AbstractMetaFunctionList lst); -static QString baseConversionString(QString typeName); - -QHash ShibokenGenerator::m_pythonPrimitiveTypeName = QHash(); -QHash ShibokenGenerator::m_pythonOperators = QHash(); -QHash ShibokenGenerator::m_formatUnits = QHash(); -QHash ShibokenGenerator::m_tpFuncs = QHash(); - - -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(); -} - -void ShibokenGenerator::clearTpFuncs() -{ - m_tpFuncs["__str__"] = QString("0"); - m_tpFuncs["__repr__"] = 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["unsigned short"] = "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(); - - // 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*="] = "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"); -} - -QString ShibokenGenerator::translateTypeForWrapperMethod(const AbstractMetaType* cType, - const AbstractMetaClass* context, - Options options) const -{ - QString result; - - if (cType->isArray()) { - result = translateTypeForWrapperMethod(cType->arrayElementType(), context, options) + "[]"; - } else { -#ifdef AVOID_PROTECTED_HACK - if (cType->isEnum()) { - const AbstractMetaEnum* metaEnum = findAbstractMetaEnum(cType); - if (metaEnum && metaEnum->isProtected()) - result = protectedEnumSurrogateName(metaEnum); - } - if (result.isEmpty()) -#endif - result = translateType(cType, context, options); - } - - return result; -} - -bool ShibokenGenerator::shouldGenerateCppWrapper(const AbstractMetaClass* metaClass) -{ - bool result = metaClass->isPolymorphic() || metaClass->hasVirtualDestructor(); -#ifdef AVOID_PROTECTED_HACK - 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(); -#endif - return result && !metaClass->isNamespace(); -} - -QString ShibokenGenerator::wrapperName(const AbstractMetaClass* metaClass) -{ - 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) -{ - if (arg->defaultValueExpression().isEmpty()) - return QString(); - - static QRegExp enumValueRegEx("^([A-Za-z_]\\w*)?$"); - QString value = arg->defaultValueExpression(); - QString prefix; - QString suffix; - - if (arg->type()->isEnum()) { - const AbstractMetaEnum* metaEnum = findAbstractMetaEnum(arg->type()); - 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 (type->isValue() || type->isObject()) - return baseConversionString(type->qualifiedCppName() + '*') + "toCpp(" + argName + ')'; - return QString(); -} - -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()); - - //TODO: check these lines - //QString modifiedReturnType = QString(func->typeReplaced(0)); - //return modifiedReturnType.isNull() ? - //translateType(func->type(), func->implementingClass()) : modifiedReturnType; -} - -static QString baseConversionString(QString typeName) -{ - return QString("Shiboken::Converter<%1 >::").arg(typeName); -} - -void ShibokenGenerator::writeBaseConversion(QTextStream& s, const TypeEntry* type) -{ - QString typeName = type->name(); - if (type->isObject()) - typeName.append('*'); -#ifdef AVOID_PROTECTED_HACK - if (type->isEnum()) { - const AbstractMetaEnum* metaEnum = findAbstractMetaEnum(type); - if (metaEnum && metaEnum->isProtected()) - typeName = protectedEnumSurrogateName(metaEnum); - } -#endif - s << baseConversionString(typeName); -} - -void ShibokenGenerator::writeBaseConversion(QTextStream& s, const AbstractMetaType* type, - const AbstractMetaClass* context, Options options) -{ - QString typeName; - if (type->isPrimitive()) { - const PrimitiveTypeEntry* ptype = (const PrimitiveTypeEntry*) type->typeEntry(); - if (ptype->basicAliasedTypeEntry()) - ptype = ptype->basicAliasedTypeEntry(); - typeName = ptype->name(); - } else { - if (type->isObject() || (type->isValue() && !type->isReference())) - options |= Generator::ExcludeConst; - if (type->isContainer() ) - options |= Generator::ExcludeReference | Generator::ExcludeConst; - typeName = translateTypeForWrapperMethod(type, context, options); - } - - s << baseConversionString(typeName); -} - -void ShibokenGenerator::writeToPythonConversion(QTextStream& s, const AbstractMetaType* type, - const AbstractMetaClass* context, const QString& argumentName) -{ - if (!type) - return; - - writeBaseConversion(s, type, context); - s << "toPython"; - - if (!argumentName.isEmpty()) - s << '(' << argumentName << ')'; -} - -void ShibokenGenerator::writeToCppConversion(QTextStream& s, const AbstractMetaClass* metaClass, - const QString& argumentName) -{ - writeBaseConversion(s, metaClass->typeEntry()); - s << "toCpp(" << argumentName << ')'; -} - -void ShibokenGenerator::writeToCppConversion(QTextStream& s, const AbstractMetaType* type, - const AbstractMetaClass* context, const QString& argumentName, - Options options) -{ - writeBaseConversion(s, type, context, options); - s << "toCpp(" << argumentName << ')'; -} - -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 ((type->isObject() || type->isValue() || type->isNamespace())) { // && !type->isReference()) { - baseName = QString("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::cpythonOperatorFunctionName(const AbstractMetaFunction* func) -{ - if (!func->isOperatorOverload()) - return QString(); - return QString("Sbk") + func->ownerClass()->name() - + '_' + pythonOperatorFunctionName(func->originalName()); -} - -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::isCString(const AbstractMetaType* type) -{ - return type->isNativePointer() && type->name() == "char"; -} - -bool ShibokenGenerator::isPairContainer(const AbstractMetaType* type) -{ - return type->isContainer() - && ((ContainerTypeEntry*)type->typeEntry())->type() == ContainerTypeEntry::PairContainer; -} - -bool ShibokenGenerator::shouldDereferenceArgumentPointer(const AbstractMetaArgument* arg) -{ - return shouldDereferenceAbstractMetaTypePointer(arg->type()); -} - -bool ShibokenGenerator::shouldDereferenceAbstractMetaTypePointer(const AbstractMetaType* metaType) -{ - const TypeEntry* type = metaType->typeEntry(); - bool isPointer = metaType->indirections() > 0; - bool isCppClass = type->isValue() || type->isObject(); - return isCppClass && !isPointer && (metaType->isValue() || metaType->isReference()); -} - -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 baseName = cpythonBaseName(metaType); - if (metaType->typeEntry()->isCustom()) { - return guessCPythonCheckFunction(metaType->typeEntry()->name()); - } else if (isNumber(baseName)) { - return genericNumberType ? "SbkNumber_Check" : baseName+"_Check"; - } else { - QString str; - QTextStream s(&str); - writeBaseConversion(s, metaType, 0); - s.flush(); - return str + "checkType"; - } -} - -QString ShibokenGenerator::cpythonCheckFunction(const TypeEntry* type, bool genericNumberType) -{ - QString baseName = cpythonBaseName(type); - if (type->isCustom()) { - return guessCPythonCheckFunction(type->name()); - } else if (isNumber(baseName)) { - return genericNumberType ? "SbkNumber_Check" : baseName+"_Check"; - } else { - QString str; - QTextStream s(&str); - writeBaseConversion(s, type); - s.flush(); - return str + "checkType"; - } -} - -QString ShibokenGenerator::guessCPythonCheckFunction(const QString& type) -{ - QString retval; - AbstractMetaType* metaType = buildAbstractMetaTypeFromString(type); - if (metaType && (metaType->name() != type)) { - retval = cpythonCheckFunction(metaType); - delete metaType; - metaType = 0; - } else if (type == "PyTypeObject") { - retval = "PyType_Check"; - } else { - retval = QString("%1_Check").arg(type); - } - if(metaType) - delete metaType; - return retval; -} - -QString ShibokenGenerator::guessCPythonIsConvertible(const QString& type) -{ - QString retval; - AbstractMetaType* metaType = buildAbstractMetaTypeFromString(type); - if (metaType) { - retval = cpythonIsConvertibleFunction(metaType); - delete metaType; - } else if (type == "PyTypeObject") { - retval = "PyType_Check"; - } else { - retval = QString("%1_Check").arg(type); - } - return retval; -} - -QString ShibokenGenerator::cpythonIsConvertibleFunction(const TypeEntry* type, bool genericNumberType, bool checkExact) -{ - if (checkExact) - return cpythonCheckFunction(type, genericNumberType); - if (type->isCustom()) - return guessCPythonIsConvertible(type->name()); - QString baseName; - QTextStream s(&baseName); - writeBaseConversion(s, type); - s << "isConvertible"; - s.flush(); - return baseName; -} - -QString ShibokenGenerator::cpythonIsConvertibleFunction(const AbstractMetaType* metaType, bool genericNumberType) -{ - - QString baseName = cpythonBaseName(metaType); - if (metaType->typeEntry()->isCustom()) { - return guessCPythonCheckFunction(metaType->typeEntry()->name()); - } else if (isNumber(baseName)) { - return genericNumberType ? "SbkNumber_Check" : baseName+"_Check"; - } else { - QString str; - QTextStream s(&str); - writeBaseConversion(s, metaType, 0); - s.flush(); - return str + "isConvertible"; - } - return baseName; -} - -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 << " self"; - } - - int argUsed = 0; - for (int i = 0; i < arguments.size(); ++i) { - if ((options & Generator::SkipRemovedArguments) && func->argumentRemoved(i+1)) - continue; - - if ((options & Generator::WriteSelf) || argUsed != 0) - s << ", "; - writeArgument(s, func, arguments[i], options); - argUsed++; - } -} - -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; - - if (argCount > 0) - s << ", "; - s << 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()) - ) - s << "_out"; - - 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 << ')'; -} - -AbstractMetaFunctionList ShibokenGenerator::filterFunctions(const AbstractMetaClass* metaClass) -{ - AbstractMetaFunctionList result; - foreach (AbstractMetaFunction *func, metaClass->functions()) { - //skip signals - if (func->isSignal() - || func->isDestructor() -#ifndef AVOID_PROTECTED_HACK - || (func->isModifiedRemoved() && !func->isAbstract())) { -#else - || (func->isModifiedRemoved() && !func->isAbstract() && !func->isProtected())) { -#endif - 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; -} - -void ShibokenGenerator::writeCodeSnips(QTextStream& s, - const CodeSnipList& codeSnips, - CodeSnip::Position position, - TypeSystem::Language language, - const AbstractMetaFunction* func, - const AbstractMetaArgument* lastArg, - const AbstractMetaClass* context) -{ - static QRegExp toPythonRegex("%CONVERTTOPYTHON\\[([^\\[]*)\\]"); - static QRegExp toCppRegex("%CONVERTTOCPP\\[([^\\[]*)\\]"); - static QRegExp pyArgsRegex("%PYARG_(\\d+)"); - - // detect is we should use pyargs instead of args as variable name for python arguments - bool usePyArgs = false; - int numArgs = 0; - if (func) { - // calc num of real arguments. - int argsRemoved = 0; - for (int i = 0; i < func->arguments().size(); i++) { - if (func->argumentRemoved(i+1)) - argsRemoved++; - } - numArgs = func->arguments().size() - argsRemoved; - usePyArgs = pythonFunctionWrapperUsesListOfArguments(OverloadData(getFunctionGroups(func->implementingClass())[func->name()], this)); - } - - foreach (CodeSnip snip, codeSnips) { - if ((position != CodeSnip::Any && snip.position != position) || !(snip.language & language)) - continue; - - QString code; - QTextStream tmpStream(&code); - formatCode(tmpStream, snip.code(), INDENT); - - if (context) { - // replace template variable for the Python Type object for the - // class context in which the variable is used - code.replace("%PYTHONTYPEOBJECT", cpythonTypeName(context) + ".super.ht_type"); - } - - // replace "toPython "converters - code.replace(toPythonRegex, "Shiboken::Converter<\\1 >::toPython"); - - // replace "toCpp "converters - code.replace(toCppRegex, "Shiboken::Converter<\\1 >::toCpp"); - - if (func) { - // replace %PYARG_# variables - code.replace("%PYARG_0", PYTHON_RETURN_VAR); - if (snip.language == TypeSystem::TargetLangCode) { - if (usePyArgs) { - code.replace(pyArgsRegex, "pyargs[\\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()); - else - code.replace("%PYARG_1", usePyArgs ? "pyargs[0]" : "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(pyargs, \\1-1, \\2)"); - - code.replace(pyArgsRegex, "PyTuple_GET_ITEM(pyargs, \\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); - } - - static QRegExp cppArgTypeRegexCheck("%ARG(\\d+)_TYPE"); - int pos = 0; - 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 = func->type()->isObject() || func->type()->isQObject() ? "%1->" : "%1."; - if (func->type()->typeEntry()->isValue() || func->type()->typeEntry()->isObject()) - code.replace("%0.", returnValueOp.arg(CPP_RETURN_VAR)); - code.replace("%0", CPP_RETURN_VAR); - } - - // replace template variable for self Python object - QString pySelf; - if (snip.language == TypeSystem::NativeCode) - pySelf = "pySelf"; - else - pySelf = "self"; - code.replace("%PYSELF", pySelf); - - // replace template variable for pointer to C++ this object - if (func->implementingClass()) { - QString cppSelf; - QString replacement("%1->"); - if (func->isStatic()) { - cppSelf = func->ownerClass()->qualifiedCppName(); - replacement = "%1::"; - } else if (snip.language == TypeSystem::NativeCode) { - cppSelf = "this"; - } else { - cppSelf = "cppSelf"; - } - code.replace("%CPPSELF.", replacement.arg(cppSelf)); - code.replace("%CPPSELF", cppSelf); - - // 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)); - } - } - - // replace template variables %# for individual arguments - int removed = 0; - for (int i = 0; i < func->arguments().size(); i++) { - const AbstractMetaArgument* arg = func->arguments().at(i); - QString argReplacement; - if (snip.language == TypeSystem::TargetLangCode) { - if (!lastArg || func->argumentRemoved(i+1)) { - if (!arg->defaultValueExpression().isEmpty()) - argReplacement = arg->defaultValueExpression(); - removed++; - } else if (lastArg && (arg->argumentIndex() > lastArg->argumentIndex())) { - argReplacement = arg->defaultValueExpression(); - } - - if (argReplacement.isEmpty()) { - if (arg->type()->typeEntry()->isCustom()) { - argReplacement = usePyArgs ? QString("pyargs[%1]").arg(i - removed) : "arg"; - } else { - argReplacement = QString("cpp_arg%1").arg(i - removed); - if (shouldDereferenceArgumentPointer(arg)) - argReplacement.prepend("(*").append(')'); - } - } - } else { - argReplacement = arg->name(); - } - code.replace("%" + QString::number(i+1), argReplacement); - } - - // replace template %ARGUMENT_NAMES variable for a list of arguments - removed = 0; - QStringList argumentNames; - foreach (const AbstractMetaArgument* arg, func->arguments()) { - if (snip.language == TypeSystem::TargetLangCode) { - if (func->argumentRemoved(arg->argumentIndex() + 1)) { - if (!arg->defaultValueExpression().isEmpty()) - argumentNames << arg->defaultValueExpression(); - removed++; - continue; - } - - QString argName; - if (lastArg && arg->argumentIndex() > lastArg->argumentIndex()) { - argName = arg->defaultValueExpression(); - } else { - argName = QString("cpp_arg%1").arg(arg->argumentIndex() - removed); - if (shouldDereferenceArgumentPointer(arg)) - argName.prepend('*'); - } - argumentNames << argName; - } else { - argumentNames << arg->name(); - } - } - code.replace("%ARGUMENT_NAMES", argumentNames.join(", ")); - - if (snip.language == TypeSystem::NativeCode) { - // replace template %PYTHON_ARGUMENTS variable for 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", "pyargs"); - - // 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", "py_override"); - } - -#ifdef AVOID_PROTECTED_HACK - // 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())); - } -#endif - - if (func->isConstructor() && shouldGenerateCppWrapper(func->ownerClass())) - code.replace("%TYPE", wrapperName(func->ownerClass())); - - replaceTemplateVariables(code, func); - } - - if (!code.isEmpty()) { - s << INDENT << "// Begin code injection" << endl; - s << code; - s << INDENT << "// End of code injection" << endl; - } - } -} - -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::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) -{ - AbstractMetaClassList baseClasses; - 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) -{ - AbstractMetaClassList result; - 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() || metaClass->typeEntry()->isObject()) - 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 typeString) -{ - typeString = typeString.trimmed(); - 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(); - } - - TypeEntry* typeEntry = TypeDatabase::instance()->findType(typeString); - AbstractMetaType* metaType = 0; - if (typeEntry) { - metaType = new AbstractMetaType(); - metaType->setTypeEntry(typeEntry); - metaType->setIndirections(indirections); - metaType->setReference(isReference); - metaType->setConstant(isConst); - if (metaType->name() == "char" && metaType->indirections() == 1) - metaType->setTypeUsagePattern(AbstractMetaType::NativePointerPattern); - } - return metaType; -} - -/* -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(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."); - return opts; -} - -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); - return true; -} - -bool ShibokenGenerator::useCtorHeuristic() const -{ - return m_useCtorHeuristic; -} - -bool ShibokenGenerator::useReturnValueHeuristic() const -{ - return m_userReturnValueHeuristic; -} - -bool ShibokenGenerator::usePySideExtensions() const -{ - return m_usePySideExtensions; -} - -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::getTypeIndexVariableName(const TypeEntry* metaType) -{ - QString res("SBK_"); - res += metaType->qualifiedCppName(); - res.replace("::", "_"); - res.replace("<", "_"); - res.replace(">", "_"); - res += "_IDX"; - return res.toUpper(); -} - -bool ShibokenGenerator::verboseErrorMessagesDisabled() const -{ - return m_verboseErrorMessagesDisabled; -} - - -bool ShibokenGenerator::pythonFunctionWrapperUsesListOfArguments(const OverloadData& overloadData) -{ - int maxArgs = overloadData.maxArgs(); - int minArgs = overloadData.minArgs(); - bool usePyArgs = (minArgs != maxArgs) - || (maxArgs > 1) - || overloadData.referenceFunction()->isConstructor() - || overloadData.hasArgumentWithDefaultValue(); - return usePyArgs; -} - diff --git a/shibokengenerator.h b/shibokengenerator.h deleted file mode 100644 index 1a7c5a63f..000000000 --- a/shibokengenerator.h +++ /dev/null @@ -1,364 +0,0 @@ -/* - * 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 SHIBOKENGENERATOR_H -#define SHIBOKENGENERATOR_H - -#define PYTHON_RETURN_VAR "pyResult" -#define CPP_RETURN_VAR "cppResult" -#define THREAD_STATE_SAVER_VAR "threadStateSaver" -#define CPP_SELF_VAR "cppSelf" - -#include -#include - -#include "overloaddata.h" - -class DocParser; - -/** - * Abstract generator that contains common methods used in CppGenerator and HeaderGenerator. - */ -class ShibokenGenerator : public Generator -{ -public: - 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; - /** - * Write a code snip into the buffer \p s. - * CodeSnip are codes inside inject-code tags. - * \param s the buffer - * \param code_snips a list of code snips - * \param position the position to insert the code snip - * \param language the kind of code snip - * \param func the cpp function - * \param lastArg last argument whose value is available, usually the last; - * a NULL pointer indicates that no argument will be available, - * i.e. a call without arguments. - * \param context the class context for the place where the code snip will be written - */ - void writeCodeSnips(QTextStream &s, - const CodeSnipList &code_snips, - CodeSnip::Position position, - TypeSystem::Language language, - const AbstractMetaFunction* func = 0, - const AbstractMetaArgument* lastArg = 0, - const AbstractMetaClass* context = 0); - - /** - * 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); - - /** - * 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); - - /// Returns a list of all ancestor classes for the given class. - AbstractMetaClassList getAllAncestors(const AbstractMetaClass* metaClass); - - const AbstractMetaClass* getMultipleInheritingClass(const AbstractMetaClass* metaClass); - - void writeBaseConversion(QTextStream& s, const AbstractMetaType* type, - const AbstractMetaClass* context, Options options = NoOption); - /// Simpler version of writeBaseConversion, uses only the base name of the type. - void writeBaseConversion(QTextStream& s, const TypeEntry* type); - void writeToPythonConversion(QTextStream& s, const AbstractMetaType* type, - const AbstractMetaClass* context, const QString& argumentName = QString()); - void writeToCppConversion(QTextStream& s, const AbstractMetaType* type, - const AbstractMetaClass* context, const QString& argumentName, Options options = NoOption); - void writeToCppConversion(QTextStream& s, const AbstractMetaClass* metaClass, const QString& argumentName); - - /// Verifies if the class should have a C++ wrapper generated for it, instead of only a Python wrapper. - static bool shouldGenerateCppWrapper(const AbstractMetaClass* metaClass); - - static QString wrapperName(const AbstractMetaClass* metaClass); - - 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 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 isCString(const AbstractMetaType* type); - static bool isPairContainer(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 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 cpythonCheckFunction(const TypeEntry* type, bool genericNumberType = false); - QString cpythonCheckFunction(const AbstractMetaType* metaType, bool genericNumberType = false); - QString guessCPythonCheckFunction(const QString& type); - QString cpythonIsConvertibleFunction(const TypeEntry* type, bool genericNumberType = false, bool checkExact = false); - QString cpythonIsConvertibleFunction(const AbstractMetaType* metaType, bool genericNumberType = false); - QString cpythonIsConvertibleFunction(const AbstractMetaArgument* metaArg, bool genericNumberType = false) - { - return cpythonIsConvertibleFunction(metaArg->type(), genericNumberType); - } - QString guessCPythonIsConvertible(const QString& 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 = "self"); - 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); - QString cpythonEnumName(const AbstractMetaEnum* metaEnum) - { - return cpythonEnumName(metaEnum->typeEntry()); - } - - QString cpythonFlagsName(const FlagsTypeEntry* flagsEntry); - 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; - QString cppApiVariableName(const QString& moduleName = QString()) const; - QString getTypeIndexVariableName(const TypeEntry* metaType); - /// 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 typeString 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 typeString); - -protected: - bool doSetup(const QMap& args); - // 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; - - void clearTpFuncs(); - - const char* name() const { return "Shiboken"; } - - /** - * Initialize correspondences between primitive and Python types - */ - static void initPrimitiveTypesCorrespondences(); - - void writeFunctionCall(QTextStream& s, - const AbstractMetaFunction* metaFunc, - Options options = NoOption) const; - - static 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 true if the Python wrapper for the received OverloadData must accept a list of arguments. - static bool pythonFunctionWrapperUsesListOfArguments(const OverloadData& overloadData); - - Indentor INDENT; -private: - bool m_useCtorHeuristic; - bool m_userReturnValueHeuristic; - bool m_usePySideExtensions; - bool m_verboseErrorMessagesDisabled; -}; - - -#endif // SHIBOKENGENERATOR_H - diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index dc5e52d4e..48d9de484 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,5 +1,28 @@ add_subdirectory(libsample) add_subdirectory(libother) + +# Detect if the python libs were compiled in debug mode +execute_process( + COMMAND ${PYTHON_EXECUTABLE} -c "from distutils import sysconfig; \\ + print sysconfig.get_config_var('Py_DEBUG')" + OUTPUT_VARIABLE PY_DEBUG + OUTPUT_STRIP_TRAILING_WHITESPACE) + +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + if(NOT PYTHON_DEBUG_LIBRARIES) + message(FATAL_ERROR "Python debug library not found. Try compile shiboken with -DCMAKE_BUILD_TYPE=Release") + endif() + if(NOT PY_DEBUG) + message(WARNING "Compiling shiboken with debug enabled, but the python executable was not compiled with debug support.") + else() + add_definitions("-DPy_DEBUG") + endif() + set(SBK_PYTHON_LIBRARIES ${PYTHON_DEBUG_LIBRARIES}) +else() + set(SBK_PYTHON_LIBRARIES ${PYTHON_LIBRARIES}) + add_definitions("-DNDEBUG") +endif() + add_subdirectory(samplebinding) add_subdirectory(otherbinding) diff --git a/tests/otherbinding/CMakeLists.txt b/tests/otherbinding/CMakeLists.txt index 25294bf74..3a16dd67c 100644 --- a/tests/otherbinding/CMakeLists.txt +++ b/tests/otherbinding/CMakeLists.txt @@ -14,7 +14,7 @@ ${CMAKE_CURRENT_BINARY_DIR}/other/other_module_wrapper.cpp ) add_custom_command(OUTPUT ${other_SRC} -COMMAND ${GENERATORRUNNER_BINARY} --generatorSet=${shiboken_BINARY_DIR}/shiboken_generator${CMAKE_RELEASE_POSTFIX}${CMAKE_DEBUG_POSTFIX} --enable-parent-ctor-heuristic +COMMAND ${GENERATORRUNNER_BINARY} --generatorSet=${generators_BINARY_DIR}/shiboken_generator${CMAKE_RELEASE_POSTFIX}${CMAKE_DEBUG_POSTFIX} --enable-parent-ctor-heuristic ${CMAKE_CURRENT_SOURCE_DIR}/global.h --include-paths=${libother_SOURCE_DIR}${PATH_SEP}${libsample_SOURCE_DIR}${PATH_SEP}${libsample_SOURCE_DIR}/.. --typesystem-paths=${CMAKE_CURRENT_SOURCE_DIR}${PATH_SEP}${sample_SOURCE_DIR} diff --git a/tests/samplebinding/CMakeLists.txt b/tests/samplebinding/CMakeLists.txt index 3536ce39b..2de6deea3 100644 --- a/tests/samplebinding/CMakeLists.txt +++ b/tests/samplebinding/CMakeLists.txt @@ -77,7 +77,7 @@ ${CMAKE_CURRENT_BINARY_DIR}/sample/valueandvirtual_wrapper.cpp ) add_custom_command(OUTPUT ${sample_SRC} -COMMAND ${GENERATORRUNNER_BINARY} --generatorSet=${shiboken_BINARY_DIR}/shiboken_generator${CMAKE_RELEASE_POSTFIX}${CMAKE_DEBUG_POSTFIX} --enable-parent-ctor-heuristic +COMMAND ${GENERATORRUNNER_BINARY} --generatorSet=${generators_BINARY_DIR}/shiboken_generator${CMAKE_RELEASE_POSTFIX}${CMAKE_DEBUG_POSTFIX} --enable-parent-ctor-heuristic ${CMAKE_CURRENT_SOURCE_DIR}/global.h --include-paths=${libsample_SOURCE_DIR} --typesystem-paths=${CMAKE_CURRENT_SOURCE_DIR} -- cgit v1.2.3