When a director method throws an exception and it is caught by DirectorException and passed back to Java using DirectorException::raiseJavaException, the Java stack trace now contains the original source line that threw the exception. Director exception handling code improved slightly to add some missing ExceptionClear calls before calling JNI code.
218 lines
6.8 KiB
OpenEdge ABL
218 lines
6.8 KiB
OpenEdge ABL
%module(directors="1") java_director_exception_feature
|
|
|
|
%include <std_except.i>
|
|
|
|
%warnfilter(SWIGWARN_TYPEMAP_DIRECTORTHROWS_UNDEF) MyNS::Foo::directorthrows_warning;
|
|
|
|
%{
|
|
#if defined(_MSC_VER)
|
|
#pragma warning(disable: 4290) // C++ exception specification ignored except to indicate a function is not __declspec(nothrow)
|
|
#endif
|
|
#if __GNUC__ >= 7
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wdeprecated" // dynamic exception specifications are deprecated in C++11
|
|
#endif
|
|
%}
|
|
|
|
%include <std_string.i>
|
|
|
|
// DEFINE exceptions in header section using std::runtime_error
|
|
%{
|
|
#include <string>
|
|
#include <exception>
|
|
#include <iostream>
|
|
|
|
namespace MyNS {
|
|
|
|
struct Exception1 : public std::runtime_error {
|
|
Exception1(const std::string& what):runtime_error(what) {}
|
|
};
|
|
struct Exception2 : public std::runtime_error {
|
|
Exception2(const std::string& what):runtime_error(what) {}
|
|
};
|
|
struct Unexpected : public std::runtime_error {
|
|
Unexpected(const std::string& what):runtime_error(what) {}
|
|
};
|
|
|
|
}
|
|
|
|
%}
|
|
|
|
// Add an explicit handler for Foo::ping, mapping one java exception back to an 'int'
|
|
%feature("director:except") MyNS::Foo::ping {
|
|
jthrowable $error = jenv->ExceptionOccurred();
|
|
if ($error) {
|
|
if (Swig::ExceptionMatches(jenv,$error,"$packagepath/MyJavaException1")) {
|
|
throw 1;
|
|
} else if (Swig::ExceptionMatches(jenv,$error,"$packagepath/MyJavaException2")) {
|
|
std::string msg(Swig::JavaExceptionMessage(jenv,$error).message());
|
|
throw MyNS::Exception2(msg);
|
|
} else {
|
|
std::cerr << "Test failed, unexpected exception thrown: " <<
|
|
Swig::JavaExceptionMessage(jenv,$error).message() << std::endl;
|
|
throw std::runtime_error("unexpected exception in Foo::ping");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Use default handler on Foo::pong, with directorthrows typemaps
|
|
|
|
// directorthrows typemaps for java->c++ conversions
|
|
%typemap(directorthrows) MyNS::Exception1,MyNS::Exception2,MyNS::Unexpected %{
|
|
if (Swig::ExceptionMatches(jenv, $error, "$packagepath/$javaclassname")) {
|
|
std::string msg(Swig::JavaExceptionMessage(jenv,$error).message());
|
|
throw $1_type(msg);
|
|
}
|
|
%}
|
|
|
|
// Override the director:except feature so exception specification is not violated
|
|
// (Cannot use built-in default of throw DirectorException)
|
|
%feature("director:except") MyNS::Foo::pong %{
|
|
jthrowable $error = jenv->ExceptionOccurred();
|
|
if ($error) {
|
|
$directorthrowshandlers
|
|
throw ::MyNS::Unexpected(Swig::JavaExceptionMessage(jenv,$error).message());
|
|
}
|
|
%}
|
|
|
|
// TODO 'throws' typemap emitted by emit_action (emit.cxx) has no way
|
|
// to get access to language specific special variables like
|
|
// $javaclassname or $packagepath ("java_director_exception_feature" here)
|
|
|
|
// throws typemaps for c++->java exception conversions
|
|
%typemap(throws,throws="MyJavaException1") MyNS::Exception1 %{
|
|
(void)$1;
|
|
jclass excpcls = jenv->FindClass("java_director_exception_feature/MyJavaException1");
|
|
if (excpcls) {
|
|
jenv->ThrowNew(excpcls, $1.what());
|
|
}
|
|
return $null;
|
|
%}
|
|
|
|
%typemap(throws,throws="MyJavaException1") int %{
|
|
(void)$1;
|
|
jclass excpcls = jenv->FindClass("java_director_exception_feature/MyJavaException1");
|
|
if (excpcls) {
|
|
jenv->ThrowNew(excpcls, "Threw some integer");
|
|
}
|
|
return $null;
|
|
%}
|
|
|
|
%typemap(throws,throws="MyJavaException2") MyNS::Exception2 %{
|
|
jclass excpcls = jenv->FindClass("java_director_exception_feature/MyJavaException2");
|
|
if (excpcls) {
|
|
jenv->ThrowNew(excpcls, $1.what());
|
|
}
|
|
return $null;
|
|
%}
|
|
|
|
%typemap(throws,throws="MyJavaUnexpected") MyNS::Unexpected %{
|
|
jclass excpcls = jenv->FindClass("java_director_exception_feature/MyJavaUnexpected");
|
|
if (excpcls) {
|
|
jenv->ThrowNew(excpcls, $1.what());
|
|
}
|
|
return $null;
|
|
%}
|
|
|
|
// Use generic exception translation approach like python, ruby
|
|
|
|
%feature("director:except") MyNS::Foo::genericpong {
|
|
jthrowable $error = jenv->ExceptionOccurred();
|
|
if ($error) {
|
|
if (Swig::ExceptionMatches(jenv,$error,"UnconstructableException")) {
|
|
// Purposefully test NULL
|
|
throw Swig::DirectorException(jenv, NULL);
|
|
}
|
|
throw Swig::DirectorException(jenv,$error);
|
|
}
|
|
}
|
|
|
|
// %exception with throws attribute. Need throws attribute for checked exceptions
|
|
%feature ("except",throws="Exception") MyNS::Foo::genericpong %{
|
|
%}
|
|
|
|
%feature ("except",throws="Exception") MyNS::Bar::genericpong %{
|
|
try { $action }
|
|
catch (Swig::DirectorException & direxcp) {
|
|
direxcp.raiseJavaException(jenv); // jenv always available in JNI code
|
|
return $null;
|
|
}
|
|
%}
|
|
|
|
|
|
|
|
%feature("director") Foo;
|
|
|
|
// Rename exceptions on java side to make translation of exceptions more clear
|
|
%rename(MyJavaException1) MyNS::Exception1;
|
|
%rename(MyJavaException2) MyNS::Exception2;
|
|
%rename(MyJavaUnexpected) MyNS::Unexpected;
|
|
|
|
%typemap(javabase) ::MyNS::Exception1,::MyNS::Exception2,::MyNS::Unexpected "java.lang.Exception";
|
|
%rename(getMessage) what() const; // Rename all what() methods
|
|
|
|
namespace MyNS {
|
|
|
|
struct Exception1 {
|
|
Exception1(const std::string& what);
|
|
const char * what() const;
|
|
};
|
|
struct Exception2 {
|
|
Exception2(const std::string& what);
|
|
const char * what() const;
|
|
};
|
|
struct Unexpected {
|
|
Unexpected(const std::string& what);
|
|
const char * what() const;
|
|
};
|
|
|
|
}
|
|
// In general it is better to use %catches instead of an exception specification on the method
|
|
// since violating an exception specification calls terminate() preventing catch-all behavior
|
|
// like throwing std::runtime_error. But an exception specification must be used if the
|
|
// actual interface being wrapped does use them.
|
|
%catches(MyNS::Exception1,MyNS::Exception2,MyNS::Unexpected) MyNS::Foo::pong;
|
|
%catches(MyNS::Exception1,MyNS::Exception2,MyNS::Unexpected) MyNS::Bar::pong;
|
|
|
|
%inline %{
|
|
|
|
namespace MyNS {
|
|
|
|
class Foo {
|
|
public:
|
|
virtual ~Foo() {}
|
|
// ping java implementation throws a java Exception1 or an Exception2 if excp is 1 or 2.
|
|
// pong java implementation throws Exception1,Exception2,Unexpected,NullPointerException for 1,2,3,4
|
|
virtual std::string ping(int excp) throw(int,MyNS::Exception2) = 0;
|
|
virtual std::string pong(int excp) /* throws MyNS::Exception1 MyNS::Exception2 MyNS::Unexpected) */ = 0;
|
|
virtual std::string genericpong(int excp) /* unspecified throws - exception is always DirectorException in C++, translated back to whatever thrown in java */ = 0;
|
|
virtual std::string directorthrows_warning(int excp) throw(double) { return std::string(); }
|
|
};
|
|
|
|
// Make a bar from a foo, so a call to Java Bar
|
|
// goes Java Bar -> C++ Bar -> C++ Foo -> Java Foo Director
|
|
|
|
class Bar {
|
|
public:
|
|
Bar(Foo* d) { delegate=d; }
|
|
virtual std::string ping(int excp) throw(int,MyNS::Exception2)
|
|
{
|
|
return delegate->ping(excp);
|
|
}
|
|
|
|
virtual std::string pong(int excp) /* throws MyNS::Exception1,MyNS::Exception2,MyNS::Unexpected */
|
|
{
|
|
return delegate->pong(excp);
|
|
}
|
|
|
|
virtual std::string genericpong(int excp)
|
|
{
|
|
return delegate->genericpong(excp);
|
|
}
|
|
|
|
private:
|
|
Foo * delegate;
|
|
};
|
|
|
|
}
|
|
%}
|