From 2412e332a9984891aa481176141d403183ac3b11 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Fri, 14 Sep 2018 11:48:10 +0200 Subject: Add exception handling Add XML elements specifying an exception handling mode to type system, complex type entries (classes) and function modifications. From the mode and the exception specification as detected by Clang, deduce whether to generate try/catch code. Task-number: PYSIDE-62 Change-Id: Ib76adc21cefd81bf9f82f819a3c151056c33a3b7 Reviewed-by: Qt CI Bot Reviewed-by: Christian Tismer --- .../shiboken2/ApiExtractor/abstractmetabuilder.cpp | 52 +++++++++++++-- .../shiboken2/ApiExtractor/abstractmetalang.cpp | 14 +++- sources/shiboken2/ApiExtractor/abstractmetalang.h | 9 ++- .../doc/typesystem_manipulating_objects.rst | 12 ++++ .../doc/typesystem_specifying_types.rst | 13 +++- .../ApiExtractor/tests/testmodifyfunction.cpp | 4 ++ sources/shiboken2/ApiExtractor/typedatabase.cpp | 3 +- sources/shiboken2/ApiExtractor/typesystem.cpp | 50 +++++++++++++- sources/shiboken2/ApiExtractor/typesystem.h | 9 +++ sources/shiboken2/ApiExtractor/typesystem_enums.h | 8 +++ sources/shiboken2/ApiExtractor/typesystem_p.h | 1 + .../shiboken2/generator/shiboken2/cppgenerator.cpp | 39 ++++++++++- sources/shiboken2/tests/libsample/CMakeLists.txt | 1 + .../shiboken2/tests/libsample/exceptiontest.cpp | 64 ++++++++++++++++++ sources/shiboken2/tests/libsample/exceptiontest.h | 48 +++++++++++++ .../shiboken2/tests/samplebinding/CMakeLists.txt | 1 + .../tests/samplebinding/exception_test.py | 78 ++++++++++++++++++++++ sources/shiboken2/tests/samplebinding/global.h | 1 + .../tests/samplebinding/typesystem_sample.xml | 2 + 19 files changed, 394 insertions(+), 15 deletions(-) create mode 100644 sources/shiboken2/tests/libsample/exceptiontest.cpp create mode 100644 sources/shiboken2/tests/libsample/exceptiontest.h create mode 100644 sources/shiboken2/tests/samplebinding/exception_test.py diff --git a/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp b/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp index 8ff2310e9..ed700e4fa 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp +++ b/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp @@ -1878,12 +1878,40 @@ bool AbstractMetaBuilderPrivate::setArrayArgumentType(AbstractMetaFunction *func return true; } +static bool generateExceptionHandling(const AbstractMetaFunction *func, + ExceptionSpecification spec, + TypeSystem::ExceptionHandling handling) +{ + switch (func->functionType()) { + case AbstractMetaFunction::CopyConstructorFunction: + case AbstractMetaFunction::MoveConstructorFunction: + case AbstractMetaFunction::AssignmentOperatorFunction: + case AbstractMetaFunction::MoveAssignmentOperatorFunction: + case AbstractMetaFunction::DestructorFunction: + return false; + default: + break; + } + switch (handling) { + case TypeSystem::ExceptionHandling::On: + return true; + case TypeSystem::ExceptionHandling::AutoDefaultToOn: + return spec != ExceptionSpecification::NoExcept; + case TypeSystem::ExceptionHandling::AutoDefaultToOff: + return spec == ExceptionSpecification::Throws; + default: + break; + } + return false; +} + AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const FunctionModelItem &functionItem) { if (functionItem->isDeleted() || !functionItem->templateParameters().isEmpty()) return nullptr; QString functionName = functionItem->name(); QString className; + TypeSystem::ExceptionHandling exceptionHandling = TypeSystem::ExceptionHandling::Unspecified; if (m_currentClass) { // Clang: Skip qt_metacast(), qt_metacall(), expanded from Q_OBJECT // and overridden metaObject(), QGADGET helpers @@ -1892,6 +1920,7 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const Functio return nullptr; } className = m_currentClass->typeEntry()->qualifiedCppName(); + exceptionHandling = m_currentClass->typeEntry()->exceptionHandling(); if (functionName == QLatin1String("metaObject") && className != QLatin1String("QObject")) return nullptr; } @@ -2050,6 +2079,19 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const Functio metaFunction->setArguments(metaArguments); + const FunctionModificationList functionMods = metaFunction->modifications(m_currentClass); + + for (const FunctionModification &mod : functionMods) { + if (mod.exceptionHandling() != TypeSystem::ExceptionHandling::Unspecified) { + exceptionHandling = mod.exceptionHandling(); + break; + } + } + + metaFunction->setGenerateExceptionHandling(generateExceptionHandling(metaFunction, + functionItem->exceptionSpecification(), + exceptionHandling)); + // Find the correct default values for (int i = 0, size = metaArguments.size(); i < size; ++i) { const ArgumentModelItem &arg = arguments.at(i); @@ -2060,9 +2102,8 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const Functio if (m_currentClass) { replacedExpression = metaFunction->replacedDefaultExpression(m_currentClass, i + 1); } else { - FunctionModificationList mods = TypeDatabase::instance()->functionModifications(metaFunction->minimalSignature()); - if (!mods.isEmpty()) { - QVector argMods = mods.constFirst().argument_mods; + if (!functionMods.isEmpty()) { + QVector argMods = functionMods.constFirst().argument_mods; if (!argMods.isEmpty()) replacedExpression = argMods.constFirst().replacedDefaultExpression; } @@ -2101,9 +2142,8 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const Functio } if (!metaArguments.isEmpty()) { - const FunctionModificationList &mods = metaFunction->modifications(m_currentClass); - fixArgumentNames(metaFunction, mods); - for (const FunctionModification &mod : mods) { + fixArgumentNames(metaFunction, functionMods); + for (const FunctionModification &mod : functionMods) { for (const ArgumentModification &argMod : mod.argument_mods) { if (argMod.array) setArrayArgumentType(metaFunction, functionItem, argMod.index - 1); diff --git a/sources/shiboken2/ApiExtractor/abstractmetalang.cpp b/sources/shiboken2/ApiExtractor/abstractmetalang.cpp index 878d7fb9e..35e12f780 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetalang.cpp +++ b/sources/shiboken2/ApiExtractor/abstractmetalang.cpp @@ -407,7 +407,8 @@ AbstractMetaFunction::AbstractMetaFunction() m_userAdded(false), m_explicit(false), m_pointerOperator(false), - m_isCallOperator(false) + m_isCallOperator(false), + m_generateExceptionHandling(false) { } @@ -526,6 +527,7 @@ AbstractMetaFunction *AbstractMetaFunction::copy() const cpy->setType(type()->copy()); cpy->setConstant(isConstant()); cpy->setExceptionSpecification(m_exceptionSpecification); + cpy->setGenerateExceptionHandling(m_generateExceptionHandling); for (AbstractMetaArgument *arg : m_arguments) cpy->addArgument(arg->copy()); @@ -1153,6 +1155,8 @@ void AbstractMetaFunction::formatDebugVerbose(QDebug &d) const d << " throw(...)"; break; } + if (m_generateExceptionHandling) + d << "[generate-exception-handling]"; d << '('; for (int i = 0, count = m_arguments.size(); i < count; ++i) { if (i) @@ -1899,6 +1903,11 @@ bool AbstractMetaClass::hasFunction(const AbstractMetaFunction *f) const return functions_contains(m_functions, f); } +bool AbstractMetaClass::generateExceptionHandling() const +{ + return queryFirstFunction(m_functions, AbstractMetaClass::Visible + | AbstractMetaClass::GenerateExceptionHandling) != nullptr; +} /* Goes through the list of functions and returns a list of all functions matching all of the criteria in \a query. */ @@ -1968,6 +1977,9 @@ bool AbstractMetaClass::queryFunction(const AbstractMetaFunction *f, FunctionQue if ((query & OperatorOverloads) && !f->isOperatorOverload()) return false; + if ((query & GenerateExceptionHandling) && !f->generateExceptionHandling()) + return false; + return true; } diff --git a/sources/shiboken2/ApiExtractor/abstractmetalang.h b/sources/shiboken2/ApiExtractor/abstractmetalang.h index 20c7da6e4..42129e9b7 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetalang.h +++ b/sources/shiboken2/ApiExtractor/abstractmetalang.h @@ -849,6 +849,9 @@ public: ExceptionSpecification exceptionSpecification() const; void setExceptionSpecification(ExceptionSpecification e); + bool generateExceptionHandling() const { return m_generateExceptionHandling; } + void setGenerateExceptionHandling(bool g) { m_generateExceptionHandling = g; } + bool isConversionOperator() const { return isConversionOperator(originalName()); @@ -1118,6 +1121,7 @@ private: uint m_explicit : 1; uint m_pointerOperator : 1; uint m_isCallOperator : 1; + uint m_generateExceptionHandling: 1; mutable int m_cachedAllowThread = -1; ExceptionSpecification m_exceptionSpecification = ExceptionSpecification::Unknown; }; @@ -1284,7 +1288,8 @@ public: VirtualInCppFunctions = 0x0020000, // Only functions that are virtual in C++ VirtualInTargetLangFunctions = 0x0080000, // Only functions which are virtual in TargetLang NotRemovedFromTargetLang = 0x0400000, // Only functions that have not been removed from TargetLang - OperatorOverloads = 0x2000000 // Only functions that are operator overloads + OperatorOverloads = 0x2000000, // Only functions that are operator overloads + GenerateExceptionHandling = 0x4000000 }; Q_DECLARE_FLAGS(FunctionQueryOptions, FunctionQueryOption) Q_FLAG(FunctionQueryOption) @@ -1386,6 +1391,8 @@ public: return (hasNonPrivateConstructor() || !hasPrivateConstructor()) && !hasPrivateDestructor(); } + bool generateExceptionHandling() const; + AbstractMetaFunctionList queryFunctionsByName(const QString &name) const; static bool queryFunction(const AbstractMetaFunction *f, FunctionQueryOptions query); static AbstractMetaFunctionList queryFunctionList(const AbstractMetaFunctionList &list, diff --git a/sources/shiboken2/ApiExtractor/doc/typesystem_manipulating_objects.rst b/sources/shiboken2/ApiExtractor/doc/typesystem_manipulating_objects.rst index 2d0c40e20..531c4ece8 100644 --- a/sources/shiboken2/ApiExtractor/doc/typesystem_manipulating_objects.rst +++ b/sources/shiboken2/ApiExtractor/doc/typesystem_manipulating_objects.rst @@ -75,6 +75,7 @@ modify-function remove="all | c++" access="public | private | protected" allow-thread="true | auto | false" + exception-handling="off | auto-off | auto-on | on" rename="..." /> @@ -92,6 +93,17 @@ modify-function The value ``auto`` means that it will be turned off for functions for which it is deemed to be safe, for example, simple getters. + The ``exception-handling`` attribute specifies whether to generate exception + handling code (nest the function call into try / catch statements). It accepts + the following values: + + * no, false: Do not generate exception handling code + * auto-off: Generate exception handling code for functions + declaring a non-empty ``throw`` list + * auto-on: Generate exception handling code unless function + declares ``noexcept`` + * yes, true: Always generate exception handling code + The ``remove``, ``access`` and ``rename`` attributes are *optional* attributes for added convenience; they serve the same purpose as the deprecated tags :ref:`remove`, :ref:`access` and :ref:`rename`. diff --git a/sources/shiboken2/ApiExtractor/doc/typesystem_specifying_types.rst b/sources/shiboken2/ApiExtractor/doc/typesystem_specifying_types.rst index c8a31a426..c3180ae88 100644 --- a/sources/shiboken2/ApiExtractor/doc/typesystem_specifying_types.rst +++ b/sources/shiboken2/ApiExtractor/doc/typesystem_specifying_types.rst @@ -11,7 +11,7 @@ typesystem .. code-block:: xml - + The **package** attribute is a string describing the package to be used, @@ -19,6 +19,9 @@ typesystem The *optional* **default-superclass** attribute is the canonical C++ base class name of all objects, e.g., "object". + The *optional* **exception-handling** attribute specifies the default exception + handling mode of all objects (see :ref:`modify-function`). + load-typesystem ^^^^^^^^^^^^^^^ @@ -216,6 +219,7 @@ value-type @@ -278,6 +286,9 @@ object-type The **revision** attribute can be used to specify a revision for each type, easing the production of ABI compatible bindings. + The *optional* **exception-handling** attribute specifies the default exception + handling mode of all functions (see :ref:`modify-function`). + interface-type ^^^^^^^^^^^^^^ diff --git a/sources/shiboken2/ApiExtractor/tests/testmodifyfunction.cpp b/sources/shiboken2/ApiExtractor/tests/testmodifyfunction.cpp index 33d75f804..af24689fe 100644 --- a/sources/shiboken2/ApiExtractor/tests/testmodifyfunction.cpp +++ b/sources/shiboken2/ApiExtractor/tests/testmodifyfunction.cpp @@ -328,6 +328,7 @@ struct A { + )XML"; QScopedPointer builder(TestUtil::parse(cppCode, xmlCode, false)); @@ -339,14 +340,17 @@ struct A { const AbstractMetaFunction *f = classA->findFunction(QStringLiteral("unspecified")); QVERIFY(f); QCOMPARE(f->exceptionSpecification(), ExceptionSpecification::Unknown); + QVERIFY(!f->generateExceptionHandling()); f = classA->findFunction(QStringLiteral("nonThrowing")); QVERIFY(f); QCOMPARE(f->exceptionSpecification(), ExceptionSpecification::NoExcept); + QVERIFY(!f->generateExceptionHandling()); f = classA->findFunction(QStringLiteral("throwing")); QVERIFY(f); QCOMPARE(f->exceptionSpecification(), ExceptionSpecification::Throws); + QVERIFY(f->generateExceptionHandling()); } QTEST_APPLESS_MAIN(TestModifyFunction) diff --git a/sources/shiboken2/ApiExtractor/typedatabase.cpp b/sources/shiboken2/ApiExtractor/typedatabase.cpp index 04eba87ca..69cddca4c 100644 --- a/sources/shiboken2/ApiExtractor/typedatabase.cpp +++ b/sources/shiboken2/ApiExtractor/typedatabase.cpp @@ -784,7 +784,8 @@ void ComplexTypeEntry::formatDebug(QDebug &d) const FORMAT_BOOL("genericClass", m_genericClass) if (m_typeFlags != 0) d << ", typeFlags=" << m_typeFlags; - d << ", copyableFlag=" << m_copyableFlag; + d << ", copyableFlag=" << m_copyableFlag + << ", except=" << int(m_exceptionHandling); FORMAT_NONEMPTY_STRING("defaultSuperclass", m_defaultSuperclass) FORMAT_NONEMPTY_STRING("polymorphicIdValue", m_polymorphicIdValue) FORMAT_NONEMPTY_STRING("lookupName", m_lookupName) diff --git a/sources/shiboken2/ApiExtractor/typesystem.cpp b/sources/shiboken2/ApiExtractor/typesystem.cpp index 20df181b6..ba219cf5f 100644 --- a/sources/shiboken2/ApiExtractor/typesystem.cpp +++ b/sources/shiboken2/ApiExtractor/typesystem.cpp @@ -64,6 +64,7 @@ static inline QString sinceAttribute() { return QStringLiteral("since"); } static inline QString defaultSuperclassAttribute() { return QStringLiteral("default-superclass"); } static inline QString deleteInMainThreadAttribute() { return QStringLiteral("delete-in-main-thread"); } static inline QString deprecatedAttribute() { return QStringLiteral("deprecated"); } +static inline QString exceptionHandlingAttribute() { return QStringLiteral("exception-handling"); } static inline QString extensibleAttribute() { return QStringLiteral("extensible"); } static inline QString flagsAttribute() { return QStringLiteral("flags"); } static inline QString forceAbstractAttribute() { return QStringLiteral("force-abstract"); } @@ -302,6 +303,18 @@ ENUM_LOOKUP_BEGIN(TypeRejection::MatchType, Qt::CaseSensitive, }; ENUM_LOOKUP_LINEAR_SEARCH() +ENUM_LOOKUP_BEGIN(TypeSystem::ExceptionHandling, Qt::CaseSensitive, + exceptionHandlingFromAttribute, TypeSystem::ExceptionHandling::Unspecified) +{ + {QStringViewLiteral("no"), TypeSystem::ExceptionHandling::Off}, + {QStringViewLiteral("false"), TypeSystem::ExceptionHandling::Off}, + {QStringViewLiteral("auto-off"), TypeSystem::ExceptionHandling::AutoDefaultToOff}, + {QStringViewLiteral("auto-on"), TypeSystem::ExceptionHandling::AutoDefaultToOn}, + {QStringViewLiteral("yes"), TypeSystem::ExceptionHandling::On}, + {QStringViewLiteral("true"), TypeSystem::ExceptionHandling::On}, +}; +ENUM_LOOKUP_LINEAR_SEARCH() + ENUM_LOOKUP_BEGIN(StackElement::ElementType, Qt::CaseInsensitive, elementFromTag, StackElement::None) { @@ -1240,6 +1253,7 @@ void Handler::applyComplexTypeAttributes(const QXmlStreamReader &reader, { bool generate = true; ctype->setCopyable(ComplexTypeEntry::Unknown); + auto exceptionHandling = m_exceptionHandling; QString package = m_defaultPackage; for (int i = attributes->size() - 1; i >= 0; --i) { @@ -1266,6 +1280,15 @@ void Handler::applyComplexTypeAttributes(const QXmlStreamReader &reader, } else if (name == copyableAttribute()) { const bool v = convertBoolean(attributes->takeAt(i).value(), copyableAttribute(), false); ctype->setCopyable(v ? ComplexTypeEntry::CopyableSet : ComplexTypeEntry::NonCopyableSet); + } else if (name == exceptionHandlingAttribute()) { + const auto attribute = attributes->takeAt(i); + const auto v = exceptionHandlingFromAttribute(attribute.value()); + if (v != TypeSystem::ExceptionHandling::Unspecified) { + exceptionHandling = v; + } else { + qCWarning(lcShiboken, "%s", + qPrintable(msgInvalidAttributeValue(attribute))); + } } else if (name == QLatin1String("held-type")) { qCWarning(lcShiboken, "%s", qPrintable(msgUnimplementedAttributeWarning(reader, name))); @@ -1285,6 +1308,9 @@ void Handler::applyComplexTypeAttributes(const QXmlStreamReader &reader, } } + if (exceptionHandling != TypeSystem::ExceptionHandling::Unspecified) + ctype->setExceptionHandling(exceptionHandling); + // The generator code relies on container's package being empty. if (ctype->type() != TypeEntry::ContainerType) ctype->setTargetLangPackage(package); @@ -1410,16 +1436,27 @@ bool Handler::parseModifyDocumentation(const QXmlStreamReader &, return true; } +// m_exceptionHandling TypeSystemTypeEntry *Handler::parseRootElement(const QXmlStreamReader &, const QVersionNumber &since, QXmlStreamAttributes *attributes) { for (int i = attributes->size() - 1; i >= 0; --i) { const QStringRef name = attributes->at(i).qualifiedName(); - if (name == packageAttribute()) + if (name == packageAttribute()) { m_defaultPackage = attributes->takeAt(i).value().toString(); - else if (name == defaultSuperclassAttribute()) + } else if (name == defaultSuperclassAttribute()) { m_defaultSuperclass = attributes->takeAt(i).value().toString(); + } else if (name == exceptionHandlingAttribute()) { + const auto attribute = attributes->takeAt(i); + const auto v = exceptionHandlingFromAttribute(attribute.value()); + if (v != TypeSystem::ExceptionHandling::Unspecified) { + m_exceptionHandling = v; + } else { + qCWarning(lcShiboken, "%s", + qPrintable(msgInvalidAttributeValue(attribute))); + } + } } TypeSystemTypeEntry *moduleEntry = @@ -1942,6 +1979,7 @@ bool Handler::parseModifyFunction(const QXmlStreamReader &reader, QString association; bool deprecated = false; bool isThread = false; + TypeSystem::ExceptionHandling exceptionHandling = TypeSystem::ExceptionHandling::Unspecified; TypeSystem::AllowThread allowThread = TypeSystem::AllowThread::Unspecified; for (int i = attributes->size() - 1; i >= 0; --i) { const QStringRef name = attributes->at(i).qualifiedName(); @@ -1970,6 +2008,13 @@ bool Handler::parseModifyFunction(const QXmlStreamReader &reader, m_error = msgInvalidAttributeValue(attribute); return false; } + } else if (name == exceptionHandlingAttribute()) { + const auto attribute = attributes->takeAt(i); + exceptionHandling = exceptionHandlingFromAttribute(attribute.value()); + if (exceptionHandling == TypeSystem::ExceptionHandling::Unspecified) { + qCWarning(lcShiboken, "%s", + qPrintable(msgInvalidAttributeValue(attribute))); + } } else if (name == virtualSlotAttribute()) { qCWarning(lcShiboken, "%s", qPrintable(msgUnimplementedAttributeWarning(reader, name))); @@ -1992,6 +2037,7 @@ bool Handler::parseModifyFunction(const QXmlStreamReader &reader, if (!mod.setSignature(signature, &m_error)) return false; mod.setOriginalSignature(originalSignature); + mod.setExceptionHandling(exceptionHandling); m_currentSignature = signature; if (!access.isEmpty()) { diff --git a/sources/shiboken2/ApiExtractor/typesystem.h b/sources/shiboken2/ApiExtractor/typesystem.h index 128b4f021..028f016f3 100644 --- a/sources/shiboken2/ApiExtractor/typesystem.h +++ b/sources/shiboken2/ApiExtractor/typesystem.h @@ -346,6 +346,9 @@ struct FunctionModification: public Modification void setOriginalSignature(const QString &s) { m_originalSignature = s; } QString originalSignature() const { return m_originalSignature; } + TypeSystem::ExceptionHandling exceptionHandling() const { return m_exceptionHandling; } + void setExceptionHandling(TypeSystem::ExceptionHandling e) { m_exceptionHandling = e; } + QString toString() const; QString association; @@ -359,6 +362,7 @@ private: QRegularExpression m_signaturePattern; bool m_thread = false; AllowThread m_allowThread = AllowThread::Unspecified; + TypeSystem::ExceptionHandling m_exceptionHandling = TypeSystem::ExceptionHandling::Unspecified; }; struct FieldModification: public Modification @@ -1370,6 +1374,9 @@ public: return m_baseContainerType; } + TypeSystem::ExceptionHandling exceptionHandling() const { return m_exceptionHandling; } + void setExceptionHandling(TypeSystem::ExceptionHandling e) { m_exceptionHandling = e; } + QString defaultConstructor() const; void setDefaultConstructor(const QString& defaultConstructor); bool hasDefaultConstructor() const; @@ -1405,6 +1412,8 @@ private: QString m_hashFunction; const ComplexTypeEntry* m_baseContainerType = nullptr; + // For class functions + TypeSystem::ExceptionHandling m_exceptionHandling = TypeSystem::ExceptionHandling::Unspecified; }; class TypedefEntry : public ComplexTypeEntry diff --git a/sources/shiboken2/ApiExtractor/typesystem_enums.h b/sources/shiboken2/ApiExtractor/typesystem_enums.h index f0ebc197d..df83429d0 100644 --- a/sources/shiboken2/ApiExtractor/typesystem_enums.h +++ b/sources/shiboken2/ApiExtractor/typesystem_enums.h @@ -90,6 +90,14 @@ enum DocModificationMode { DocModificationInvalid }; +enum class ExceptionHandling { + Unspecified, + Off, + AutoDefaultToOff, + AutoDefaultToOn, + On +}; + } // namespace TypeSystem #endif // TYPESYSTEM_ENUMS_H diff --git a/sources/shiboken2/ApiExtractor/typesystem_p.h b/sources/shiboken2/ApiExtractor/typesystem_p.h index 2bd7cfadd..e36df5151 100644 --- a/sources/shiboken2/ApiExtractor/typesystem_p.h +++ b/sources/shiboken2/ApiExtractor/typesystem_p.h @@ -242,6 +242,7 @@ private: int m_ignoreDepth = 0; QString m_defaultPackage; QString m_defaultSuperclass; + TypeSystem::ExceptionHandling m_exceptionHandling = TypeSystem::ExceptionHandling::Unspecified; QString m_error; const TypeEntry::CodeGeneration m_generate; diff --git a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp index 2d4ea068d..0f94793e9 100644 --- a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp @@ -301,6 +301,8 @@ void CppGenerator::generateClass(QTextStream &s, GeneratorContext &classContext) // needs the 'set' class from C++ STL. if (hasMultipleInheritanceInAncestry(metaClass)) s << "#include " << endl; + if (metaClass->generateExceptionHandling()) + s << "#include " << endl; s << endl << "// module include" << endl << "#include \"" << getModuleHeaderFileName() << '"' << endl; @@ -3077,6 +3079,17 @@ QString CppGenerator::argumentNameFromIndex(const AbstractMetaFunction* func, in return pyArgName; } +static QStringList defaultExceptionHandling() +{ + static const QStringList result{ + QLatin1String("} catch (const std::exception &e) {"), + QLatin1String(" PyErr_SetString(PyExc_RuntimeError, e.what());"), + QLatin1String("} catch (...) {"), + QLatin1String(" PyErr_SetString(PyExc_RuntimeError, \"An unknown exception was caught\");"), + QLatin1String("}")}; + return result; +} + void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *func, GeneratorContext &context, int maxArgs) { @@ -3334,8 +3347,17 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f if (!injectedCodeCallsCppFunction(func)) { const bool allowThread = func->allowThread(); - if (allowThread) + const bool generateExceptionHandling = func->generateExceptionHandling(); + if (generateExceptionHandling) { + s << INDENT << "try {\n"; + ++INDENT.indent; + if (allowThread) { + s << INDENT << "Shiboken::ThreadStateSaver threadSaver;\n" + << INDENT << "threadSaver.save();\n"; + } + } else if (allowThread) { s << INDENT << BEGIN_ALLOW_THREADS << endl; + } s << INDENT; if (isCtor) { s << (useVAddr.isEmpty() ? @@ -3369,9 +3391,13 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f } else { s << methodCall << ';' << endl; } - if (allowThread) - s << INDENT << END_ALLOW_THREADS << endl; + if (allowThread) { + s << INDENT << (generateExceptionHandling + ? "threadSaver.restore();" : END_ALLOW_THREADS) << '\n'; + } + + // Convert result if (!func->conversionRule(TypeSystem::TargetLangCode, 0).isEmpty()) { writeConversionRule(s, func, TypeSystem::TargetLangCode, QLatin1String(PYTHON_RETURN_VAR)); } else if (!isCtor && !func->isInplaceOperator() && func->type() @@ -3385,6 +3411,13 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f } s << ';' << endl; } + + if (generateExceptionHandling) { // "catch" code + --INDENT.indent; + const QStringList handlingCode = defaultExceptionHandling(); + for (const auto &line : handlingCode) + s << INDENT << line << '\n'; + } } } diff --git a/sources/shiboken2/tests/libsample/CMakeLists.txt b/sources/shiboken2/tests/libsample/CMakeLists.txt index 7bbc0c3dd..ae3d40312 100644 --- a/sources/shiboken2/tests/libsample/CMakeLists.txt +++ b/sources/shiboken2/tests/libsample/CMakeLists.txt @@ -10,6 +10,7 @@ complex.cpp onlycopy.cpp derived.cpp echo.cpp +exceptiontest.cpp functions.cpp handle.cpp implicitconv.cpp diff --git a/sources/shiboken2/tests/libsample/exceptiontest.cpp b/sources/shiboken2/tests/libsample/exceptiontest.cpp new file mode 100644 index 000000000..1302a8e43 --- /dev/null +++ b/sources/shiboken2/tests/libsample/exceptiontest.cpp @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of Qt for Python. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "exceptiontest.h" + +class TestException : public std::exception +{ +public: + const char *what() const noexcept override + { return "TestException"; } +}; + +ExceptionTest::ExceptionTest() = default; + +int ExceptionTest::intThrowStdException(bool doThrow) +{ + if (doThrow) + throw TestException(); + return 1; +} + +void ExceptionTest::voidThrowStdException(bool doThrow) +{ + if (doThrow) + throw TestException(); +} + +int ExceptionTest::intThrowInt(bool doThrow) +{ + if (doThrow) + throw 42; + return 1; +} + +void ExceptionTest::voidThrowInt(bool doThrow) +{ + if (doThrow) + throw 42; +} diff --git a/sources/shiboken2/tests/libsample/exceptiontest.h b/sources/shiboken2/tests/libsample/exceptiontest.h new file mode 100644 index 000000000..8ab3e2b67 --- /dev/null +++ b/sources/shiboken2/tests/libsample/exceptiontest.h @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of Qt for Python. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef EXCEPTIONTEST_H +#define EXCEPTIONTEST_H + +#include "libsamplemacros.h" + +#include + +class LIBSAMPLE_API ExceptionTest +{ + public: + ExceptionTest(); + + int intThrowStdException(bool doThrow); + void voidThrowStdException(bool doThrow); + + int intThrowInt(bool doThrow); + void voidThrowInt(bool doThrow); +}; + +#endif // EXCEPTIONTEST_H diff --git a/sources/shiboken2/tests/samplebinding/CMakeLists.txt b/sources/shiboken2/tests/samplebinding/CMakeLists.txt index 4a2b70b57..ca737ca08 100644 --- a/sources/shiboken2/tests/samplebinding/CMakeLists.txt +++ b/sources/shiboken2/tests/samplebinding/CMakeLists.txt @@ -29,6 +29,7 @@ ${CMAKE_CURRENT_BINARY_DIR}/sample/derived_someinnerclass_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/echo_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/event_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/expression_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/exceptiontest_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/friendofonlycopy_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/handleholder_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/implicitconv_wrapper.cpp diff --git a/sources/shiboken2/tests/samplebinding/exception_test.py b/sources/shiboken2/tests/samplebinding/exception_test.py new file mode 100644 index 000000000..d6c02433a --- /dev/null +++ b/sources/shiboken2/tests/samplebinding/exception_test.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python +# +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the test suite of Qt for Python. +## +## $QT_BEGIN_LICENSE:GPL-EXCEPT$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 as published by the Free Software +## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +import unittest + +from sample import ExceptionTest + +class CppExceptionTest(unittest.TestCase): + + def testVoid(self): + exceptionCount = 0 + et = ExceptionTest() + + et.voidThrowStdException(False) + + try: + et.voidThrowStdException(True) + except: + exceptionCount += 1 + + et.voidThrowInt(False) + + try: + et.voidThrowInt(True) + except: + exceptionCount += 1 + + self.assertEqual(exceptionCount, 2) + + def testReturnValue(self): + exceptionCount = 0 + et = ExceptionTest() + + result = et.intThrowStdException(False); + + try: + result = et.intThrowStdException(True); + except: + exceptionCount += 1 + + result = et.intThrowInt(False); + + try: + result = et.intThrowInt(True); + except: + exceptionCount += 1 + + self.assertEqual(exceptionCount, 2) + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken2/tests/samplebinding/global.h b/sources/shiboken2/tests/samplebinding/global.h index 1bccc4c66..3984102a8 100644 --- a/sources/shiboken2/tests/samplebinding/global.h +++ b/sources/shiboken2/tests/samplebinding/global.h @@ -37,6 +37,7 @@ #include "sbkdate.h" #include "derived.h" #include "echo.h" +#include "exceptiontest.h" #include "functions.h" #include "implicitconv.h" #include "nontypetemplate.h" diff --git a/sources/shiboken2/tests/samplebinding/typesystem_sample.xml b/sources/shiboken2/tests/samplebinding/typesystem_sample.xml index 6f92dcb2f..334f0162d 100644 --- a/sources/shiboken2/tests/samplebinding/typesystem_sample.xml +++ b/sources/shiboken2/tests/samplebinding/typesystem_sample.xml @@ -2392,6 +2392,8 @@ + + -- cgit v1.2.3