diff options
author | Hugo Parente Lima <hugo.pl@gmail.com> | 2010-09-20 17:26:09 -0300 |
---|---|---|
committer | Hugo Parente Lima <hugo.pl@gmail.com> | 2012-03-08 16:06:00 -0300 |
commit | 7f5723eac1366e901391fb5771f4a70a45dc6932 (patch) | |
tree | 85ed4c047460cec62d8cc08d35ec4a811f96e1c1 /generator | |
parent | 76dae669fda417b539315b9f439128f5b29c725e (diff) |
Shiboken generator code moved to the directory generator.
Reviewer: Renato Araújo <renato.filho@openbossa.org>
Luciano Wolf <luciano.wolf@openbossa.org>
Diffstat (limited to 'generator')
-rw-r--r-- | generator/cppgenerator.cpp | 3849 | ||||
-rw-r--r-- | generator/cppgenerator.h | 201 | ||||
-rw-r--r-- | generator/headergenerator.cpp | 610 | ||||
-rw-r--r-- | generator/headergenerator.h | 58 | ||||
-rw-r--r-- | generator/main.cpp | 34 | ||||
-rw-r--r-- | generator/overloaddata.cpp | 788 | ||||
-rw-r--r-- | generator/overloaddata.h | 147 | ||||
-rw-r--r-- | generator/shiboken.cpp | 27 | ||||
-rw-r--r-- | generator/shibokengenerator.cpp | 1651 | ||||
-rw-r--r-- | generator/shibokengenerator.h | 364 |
10 files changed, 7729 insertions, 0 deletions
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 <contact@pyside.org> + * + * 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 <reporthandler.h> +#include <typedatabase.h> + +#include <QtCore/QDir> +#include <QtCore/QTextStream> +#include <QtCore/QDebug> + +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<QString, QString> 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<AbstractMetaFunctionList> CppGenerator::filterGroupedOperatorFunctions(const AbstractMetaClass* metaClass, + uint query) +{ + // ( func_name, num_args ) => func_list + QMap<QPair<QString, int >, 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<QString, int > 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 <shiboken.h>" << endl; + if (usePySideExtensions()) { + s << "#include <qsignal.h>" << endl; + s << "#include <qproperty.h>" << endl; + s << "#include <pyside.h>" << endl; + } + + s << "#include <typeresolver.h>\n"; + s << "#include <typeinfo>\n"; + if (usePySideExtensions()) { + if (metaClass->isQObject()) { + s << "#include <signalmanager.h>\n"; + s << "#include <dynamicqmetaobject.h>\n"; + } + } + + // The multiple inheritance initialization function + // needs the 'set' class from C++ STL. + if (hasMultipleInheritanceInAncestry(metaClass)) + s << "#include <set>" << 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<Include> 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<AbstractMetaFunctionList> 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<char*>(\"" << 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<enum>) + 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<const ContainerTypeEntry*>(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<Shiboken::SbkBaseWrapper*>(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<Shiboken::SbkBaseWrapper*>(pySelf), m_metaObject, PySide::deleteDynamicQMetaObject);" << endl; + } + s << INDENT << "} else {" << endl; + { + Indentation indentation2(INDENT); + s << INDENT << "m_metaObject = reinterpret_cast<PySide::DynamicQMetaObject*>(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<QString> 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<SbkBaseWrapper*>(self);" << endl; + + if (metaClass->isAbstract() || metaClass->baseClassNames().size() > 1) { + s << INDENT << "SbkBaseWrapperType* type = reinterpret_cast<SbkBaseWrapperType*>(self->ob_type);" << endl; + s << INDENT << "SbkBaseWrapperType* myType = reinterpret_cast<SbkBaseWrapperType*>(" << 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<const PrimitiveTypeEntry*>(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<char*>(\"" << 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<char*>(\"" << revOpName << "\"));" << endl; + s << INDENT << "if (revOpMethod && PyCallable_Check(revOpMethod)) {" << endl; + { + Indentation indent(INDENT); + s << INDENT << PYTHON_RETURN_VAR " = PyObject_CallFunction(revOpMethod, const_cast<char*>(\"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<int> 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<const PrimitiveTypeEntry*>(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<const TypeEntry*> 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<QString>::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<const AbstractMetaFunction*> 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<const AbstractMetaFunction*> 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<AbstractMetaFunction* const>(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<ArgumentModification> ownership_mods; + // Python object reference management. + QList<ArgumentModification> 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<SbkBaseWrapper*>(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<const %1*>(class_ptr)) - base").arg(baseClass->qualifiedCppName())); + result.append(QString("((size_t) static_cast<const %1*>((%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<int> offsets;" << endl; + s << INDENT << "std::set<int>::iterator it;" << endl; + s << INDENT << "const " << className << "* class_ptr = reinterpret_cast<const " << className << "*>(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<SbkBaseWrapperType*>(" << 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<const AbstractMetaClass*>& 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<const AbstractMetaClass*>& 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<const AbstractMetaClass*>& conversions) +{ + s << INDENT << "// Extended implicit conversions for " << externalType->targetLangPackage() << '.' << externalType->name() << endl; + s << INDENT << "shiboType = reinterpret_cast<Shiboken::SbkBaseWrapperType*>("; + 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<const ContainerTypeEntry*>(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<PyTypeObject*>(&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<QString, QString> 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<QString, QString> 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<QString, QString> 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<AbstractMetaFunctionList> 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<SbkBaseWrapper*>(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<AbstractMetaFunctionList> 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<int>(\""; + if (cppEnum->enclosingClass()) + s << cppEnum->enclosingClass()->qualifiedCppName() << "::"; + s << cppEnum->name() << "\");\n"; + } + + + s << endl; +} + +void CppGenerator::writeSignalInitialization(QTextStream& s, const AbstractMetaClass* metaClass) +{ + QHash<QString, QStringList> 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<char*>(\"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<PyTypeObject*>(&" << 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<SbkBaseWrapperType*>(" + 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<SbkBaseWrapperType*>(" << 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<PyObject*>(&" << 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<SbkBaseWrapperType*>(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::SbkBaseWrapperType*>(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<Include> 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 <Python.h>" << endl; + s << "#include <shiboken.h>" << endl; + s << "#include <algorithm>" << endl; + + s << "#include \"" << getModuleHeaderFileName() << '"' << endl << endl; + foreach (const Include& include, includes) + s << include; + s << endl; + + TypeSystemTypeEntry* moduleEntry = reinterpret_cast<TypeSystemTypeEntry*>(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<void*>(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<QString> 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_cast<const " << metaClass->qualifiedCppName() << "*>(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 <contact@pyside.org> + * + * 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<AbstractMetaFunctionList> 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<const AbstractMetaClass*>& conversions); + void writeExtendedToCppFunction(QTextStream& s, const TypeEntry* externalType, const QList<const AbstractMetaClass*>& conversions); + void writeExtendedConverterInitialization(QTextStream& s, const TypeEntry* externalType, const QList<const AbstractMetaClass*>& conversions); + + void writeParentChildManagement(QTextStream& s, const AbstractMetaFunction* func, bool userHeuristicForReturn); + bool writeParentChildManagement(QTextStream& s, const AbstractMetaFunction* func, int argIndex, bool userHeuristicPolicy); + void writeReturnValueHeuristics(QTextStream& s, const AbstractMetaFunction* func, 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<QString, QPair<QString, QString> > 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 <contact@pyside.org> + * + * 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 <typedatabase.h> +#include <reporthandler.h> +#include <fileout.h> + +#include <QtCore/QDir> +#include <QtCore/QTextStream> +#include <QtCore/QVariant> +#include <QtCore/QRegExp> +#include <QtCore/QDebug> + +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 <shiboken.h>" << 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<const FlagsTypeEntry*>(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<const EnumTypeEntry*>(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<Include> 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 <Python.h>" << endl; + s << "#include <conversions.h>" << endl; + s << "#include <pyenum.h>" << endl; + s << "#include <basewrapper.h>" << endl; + s << "#include <bindingmanager.h>" << endl; + s << "#include <memory>" << endl << endl; + if (usePySideExtensions()) + s << "#include <qsignal.h>" << 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<SbkBaseWrapperType*>(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<PyTypeObject*>(" << 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<T>::toCpp method is only need for + // classes with implicit conversions. + if (!hasImplicitConversions) + return; + + // Write Converter<T>::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<SbkBaseWrapperType*>(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<T>::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<SbkBaseWrapperType*>(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 <contact@pyside.org> + * + * 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<QString, QString> options() const { return QMap<QString, QString>(); } +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 <contact@pyside.org> + * + * 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 <QtCore> + +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 <contact@pyside.org> + * + * 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 <QtCore/QFile> +#include <reporthandler.h> +#include <graph.h> +#include "overloaddata.h" +#include "shibokengenerator.h" + +static const TypeEntry* getAliasedTypeEntry(const TypeEntry* typeEntry) +{ + if (typeEntry->isPrimitive()) { + const PrimitiveTypeEntry* pte = reinterpret_cast<const PrimitiveTypeEntry*>(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<QString, int> map; // type_name -> id + QHash<int, OverloadData*> 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<int> 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<QString> 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<const AbstractMetaFunction*> OverloadData::overloadsWithoutRepetition() const +{ + QList<const AbstractMetaFunction*> 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<int> OverloadData::invalidArgumentLengths() const +{ + QSet<int> 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<int> invalidArgLengths; + for (int i = minArgs() + 1; i < maxArgs(); i++) { + if (!validArgLengths.contains(i)) + invalidArgLengths.append(i); + } + + return invalidArgLengths; +} + +int OverloadData::numberOfRemovedArguments(const AbstractMetaFunction* func, int finalArgPos) +{ + int removed = 0; + if (finalArgPos < 0) { + for (int i = 0; i < func->arguments().size(); i++) { + if (func->argumentRemoved(i + 1)) + removed++; + } + } else { + for (int i = 0; i < finalArgPos + removed; i++) { + if (func->argumentRemoved(i + 1)) + removed++; + } + } + return removed; +} + +QPair<int, int> OverloadData::getMinMaxArguments(const AbstractMetaFunctionList& overloads) +{ + int minArgs = 10000; + int maxArgs = 0; + for (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<int, int>(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=<<table border=\"0\" cellborder=\"0\" cellpadding=\"3\" bgcolor=\"white\">"; + s << "<tr><td bgcolor=\"black\" align=\"center\" cellpadding=\"6\" colspan=\"2\"><font color=\"white\">"; + if (rfunc->ownerClass()) + s << rfunc->ownerClass()->name() << "::"; + s << rfunc->name().replace('<', "<").replace('>', ">") << "</font>"; + if (rfunc->isVirtual()) { + s << "<br/><font color=\"white\" point-size=\"10\"><<"; + if (rfunc->isAbstract()) + s << "pure "; + s << "virtual>></font>"; + } + s << "</td></tr>"; + + // Function return type + s << "<tr><td bgcolor=\"gray\" align=\"right\">original type</td><td bgcolor=\"gray\" align=\"left\">"; + if (rfunc->type()) + s << rfunc->type()->cppSignature().replace('<', "<").replace('>', ">"); + else + s << "void"; + s << "</td></tr>"; + + // Shows type changes for all function signatures + foreach (const AbstractMetaFunction* func, overloads()) { + if (func->typeReplaced(0).isEmpty()) + continue; + s << "<tr><td bgcolor=\"gray\" align=\"right\">f" << functionNumber(func); + s << "-type</td><td bgcolor=\"gray\" align=\"left\">"; + s << func->typeReplaced(0).replace('<', "<").replace('>', ">") << "</td></tr>"; + } + + // Minimum and maximum number of arguments + s << "<tr><td bgcolor=\"gray\" align=\"right\">minArgs</td><td bgcolor=\"gray\" align=\"left\">"; + s << minArgs() << "</td></tr>"; + s << "<tr><td bgcolor=\"gray\" align=\"right\">maxArgs</td><td bgcolor=\"gray\" align=\"left\">"; + s << maxArgs() << "</td></tr>"; + + if (rfunc->ownerClass()) { + if (rfunc->implementingClass() != rfunc->ownerClass()) + s << "<tr><td align=\"right\">implementor</td><td align=\"left\">" << rfunc->implementingClass()->name() << "</td></tr>"; + if (rfunc->declaringClass() != rfunc->ownerClass() && rfunc->declaringClass() != rfunc->implementingClass()) + s << "<tr><td align=\"right\">declarator</td><td align=\"left\">" << rfunc->declaringClass()->name() << "</td></tr>"; + } + + // Overloads for the signature to present point + s << "<tr><td bgcolor=\"gray\" align=\"right\">overloads</td><td bgcolor=\"gray\" align=\"left\">"; + foreach (const AbstractMetaFunction* func, overloads()) + s << 'f' << functionNumber(func) << ' '; + s << "</td></tr>"; + + s << "</table>> ];" << 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=<<table border=\"0\" cellborder=\"0\" cellpadding=\"3\" bgcolor=\"white\">"; + + // Argument box title + s << "<tr><td bgcolor=\"black\" align=\"left\" cellpadding=\"2\" colspan=\"2\">"; + s << "<font color=\"white\" point-size=\"11\">arg #" << argPos() << "</font></td></tr>"; + + // Argument type information + QString type = hasArgumentTypeReplace() ? argumentTypeReplaced() : argType()->cppSignature(); + s << "<tr><td bgcolor=\"gray\" align=\"right\">type</td><td bgcolor=\"gray\" align=\"left\">"; + s << type.replace("&", "&") << "</td></tr>"; + if (hasArgumentTypeReplace()) { + s << "<tr><td bgcolor=\"gray\" align=\"right\">orig. type</td><td bgcolor=\"gray\" align=\"left\">"; + s << argType()->cppSignature().replace("&", "&") << "</td></tr>"; + } + + // Overloads for the signature to present point + s << "<tr><td bgcolor=\"gray\" align=\"right\">overloads</td><td bgcolor=\"gray\" align=\"left\">"; + foreach (const AbstractMetaFunction* func, overloads()) + s << 'f' << functionNumber(func) << ' '; + s << "</td></tr>"; + + // 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 << "<tr><td bgcolor=\"gray\" align=\"right\">f" << functionNumber(func); + s << "-default</td><td bgcolor=\"gray\" align=\"left\">"; + s << arg->defaultValueExpression() << "</td></tr>"; + } + if (arg->defaultValueExpression() != arg->originalDefaultValueExpression()) { + s << "<tr><td bgcolor=\"gray\" align=\"right\">f" << functionNumber(func); + s << "-orig-default</td><td bgcolor=\"gray\" align=\"left\">"; + s << arg->originalDefaultValueExpression() << "</td></tr>"; + } + } + + s << "</table>>];" << 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 <contact@pyside.org> + * + * 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 <abstractmetalang.h> +#include <QtCore/QList> +#include <QtCore/QBitArray> + +class ShibokenGenerator; + +class OverloadData; +typedef QList<OverloadData*> 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<const AbstractMetaFunction*> overloadsWithoutRepetition() const; + const QList<const AbstractMetaFunction*>& overloads() const { return m_overloads; } + OverloadDataList nextOverloadData() const { return m_nextOverloadData; } + OverloadData* previousOverloadData() const { return m_previousOverloadData; } + + QList<int> invalidArgumentLengths() const; + + static int numberOfRemovedArguments(const AbstractMetaFunction* func, int finalArgPos = -1); + static QPair<int, int> getMinMaxArguments(const AbstractMetaFunctionList& overloads); + /// Returns true if all overloads have no more than one argument. + static bool isSingleArgument(const AbstractMetaFunctionList& overloads); + + void dumpGraph(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<const AbstractMetaFunction*> 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 <contact@pyside.org> + * + * 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 <contact@pyside.org> + * + * 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 <reporthandler.h> +#include <typedatabase.h> + +#include <QtCore/QDir> +#include <QtCore/QDebug> +#include <limits> + +#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<QString, QString> ShibokenGenerator::m_pythonPrimitiveTypeName = QHash<QString, QString>(); +QHash<QString, QString> ShibokenGenerator::m_pythonOperators = QHash<QString, QString>(); +QHash<QString, QString> ShibokenGenerator::m_formatUnits = QHash<QString, QString>(); +QHash<QString, QString> ShibokenGenerator::m_tpFuncs = QHash<QString, QString>(); + + +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<ReferenceCount> 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<QString, AbstractMetaFunctionList> 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<int>::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<QString, QString> ShibokenGenerator::options() const +{ + QMap<QString, QString> 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<QString, QString>& 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 <contact@pyside.org> + * + * 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 <generator.h> +#include <QtCore/QTextStream> + +#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<QString, AbstractMetaFunctionList> 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<int, int> 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<QString, QString>& args); + // verify whether the class is copyable + bool isCopyable(const AbstractMetaClass* metaClass); + + bool m_native_jump_table; + static QHash<QString, QString> m_pythonPrimitiveTypeName; + static QHash<QString, QString> m_pythonOperators; + static QHash<QString, QString> m_formatUnits; + static QHash<QString, QString> 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<const TypeEntry*, QList<const AbstractMetaClass*> > 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 + |