aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@qt.io>2018-09-14 11:48:10 +0200
committerFriedemann Kleint <Friedemann.Kleint@qt.io>2018-09-27 06:16:46 +0000
commit2412e332a9984891aa481176141d403183ac3b11 (patch)
tree5646f4a929a3585131eb09009f404834a55c6cd2
parent0f8b1cf2daae6fcc0a922a9e2514f12374eeee31 (diff)
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 <qt_ci_bot@qt-project.org> Reviewed-by: Christian Tismer <tismer@stackless.com>
-rw-r--r--sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp52
-rw-r--r--sources/shiboken2/ApiExtractor/abstractmetalang.cpp14
-rw-r--r--sources/shiboken2/ApiExtractor/abstractmetalang.h9
-rw-r--r--sources/shiboken2/ApiExtractor/doc/typesystem_manipulating_objects.rst12
-rw-r--r--sources/shiboken2/ApiExtractor/doc/typesystem_specifying_types.rst13
-rw-r--r--sources/shiboken2/ApiExtractor/tests/testmodifyfunction.cpp4
-rw-r--r--sources/shiboken2/ApiExtractor/typedatabase.cpp3
-rw-r--r--sources/shiboken2/ApiExtractor/typesystem.cpp50
-rw-r--r--sources/shiboken2/ApiExtractor/typesystem.h9
-rw-r--r--sources/shiboken2/ApiExtractor/typesystem_enums.h8
-rw-r--r--sources/shiboken2/ApiExtractor/typesystem_p.h1
-rw-r--r--sources/shiboken2/generator/shiboken2/cppgenerator.cpp39
-rw-r--r--sources/shiboken2/tests/libsample/CMakeLists.txt1
-rw-r--r--sources/shiboken2/tests/libsample/exceptiontest.cpp64
-rw-r--r--sources/shiboken2/tests/libsample/exceptiontest.h48
-rw-r--r--sources/shiboken2/tests/samplebinding/CMakeLists.txt1
-rw-r--r--sources/shiboken2/tests/samplebinding/exception_test.py78
-rw-r--r--sources/shiboken2/tests/samplebinding/global.h1
-rw-r--r--sources/shiboken2/tests/samplebinding/typesystem_sample.xml2
19 files changed, 394 insertions, 15 deletions
diff --git a/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp b/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp
index 8ff2310e..ed700e4f 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<ArgumentModification> argMods = mods.constFirst().argument_mods;
+ if (!functionMods.isEmpty()) {
+ QVector<ArgumentModification> 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 878d7fb9..35e12f78 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 20c7da6e..42129e9b 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 2d0c40e2..531c4ece 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="..." />
</object-type>
@@ -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 c8a31a42..c3180ae8 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
- <typesystem package="..." default-superclass="...">
+ <typesystem package="..." default-superclass="..." exception-handling="...">
</typesystem>
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
<typesystem>
<value-type name="..." since="..."
copyable="yes | no"
+ exception-handling="..."
hash-function="..."
stream="yes | no"
default-constructor="..."
@@ -243,6 +247,9 @@ value-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`).
+
.. _object-type:
object-type
@@ -258,6 +265,7 @@ object-type
<object-type name="..."
since="..."
copyable="yes | no"
+ exception-handling="..."
hash-function="..."
stream="yes | no"
revision="..." />
@@ -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 33d75f80..af24689f 100644
--- a/sources/shiboken2/ApiExtractor/tests/testmodifyfunction.cpp
+++ b/sources/shiboken2/ApiExtractor/tests/testmodifyfunction.cpp
@@ -328,6 +328,7 @@ struct A {
<typesystem package="Foo">
<primitive-type name='int'/>
<object-type name='A'>
+ <modify-function signature='throwing()' exception-handling='auto-on'/>
</object-type>
</typesystem>)XML";
QScopedPointer<AbstractMetaBuilder> 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 04eba87c..69cddca4 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 20df181b..ba219cf5 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 128b4f02..028f016f 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 f0ebc197..df83429d 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 2bd7cfad..e36df515 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 2d4ea068..0f94793e 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 <set>" << endl;
+ if (metaClass->generateExceptionHandling())
+ s << "#include <exception>" << 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 7bbc0c3d..ae3d4031 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 00000000..1302a8e4
--- /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 00000000..8ab3e2b6
--- /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 <exception>
+
+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 4a2b70b5..ca737ca0 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 00000000..d6c02433
--- /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 1bccc4c6..3984102a 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 6f92dcb2..334f0162 100644
--- a/sources/shiboken2/tests/samplebinding/typesystem_sample.xml
+++ b/sources/shiboken2/tests/samplebinding/typesystem_sample.xml
@@ -2392,6 +2392,8 @@
<value-type name="Expression" />
+ <object-type name="ExceptionTest" exception-handling="auto-on"/>
+
<value-type name="ModelIndex" />
<value-type name="ReferentModelIndex">
<modify-function signature="operator const ModelIndex&amp;()const">