diff --git a/Doc/Manual/Java.html b/Doc/Manual/Java.html index 69cdc4eba..664ccb7d2 100644 --- a/Doc/Manual/Java.html +++ b/Doc/Manual/Java.html @@ -3843,7 +3843,6 @@ handle the mapping of Java exceptions into C++ exceptions. %feature("director:except") MyClass::method(int x) { jthrowable $error = jenv->ExceptionOccurred(); if ($error) { - jenv->ExceptionClear(); if (Swig::ExceptionMatches(jenv, $error, "java/lang/IndexOutOfBoundsException")) throw std::out_of_range(Swig::JavaExceptionMessage(jenv, $error).message()); if (Swig::ExceptionMatches(jenv, $error, "$packagepath/MyJavaException")) @@ -3921,7 +3920,6 @@ is used in the feature code. Consider the following, which also happens to be th %feature("director:except") %{ jthrowable $error = jenv->ExceptionOccurred(); if ($error) { - jenv->ExceptionClear(); $directorthrowshandlers Swig::DirectorException::raise(jenv, $error); } @@ -3970,7 +3968,6 @@ the resulting code generated in the director method after calling up to Java wil
 jthrowable swigerror = jenv->ExceptionOccurred();
 if (swigerror) {
-  jenv->ExceptionClear();
   if (Swig::ExceptionMatches(jenv, swigerror, "java/lang/IndexOutOfBoundsException")) {
     throw std::out_of_range(Swig::JavaExceptionMessage(jenv, swigerror).message());
   }
@@ -3992,7 +3989,7 @@ this potential fate.
 

Because the default code generation maps any unhandled Java exceptions to Swig::DirectorException, any director methods that have exception specifications may cause program termination. To simply ignore -unexpected exceptions, the default handling can be changed with: +unexpected exceptions, the default handling can be changed by adding:

@@ -4068,14 +4065,10 @@ DEFINE_EXCEPTION(ExceptionA) DEFINE_EXCEPTION(ExceptionB) DEFINE_EXCEPTION(Unexpected) -// Mark three methods to map director thrown exceptions. -%feature("director:except") MyClass::meth1(int); -%feature("director:except") MyClass::meth2; -%feature("director:except") meth3; - %typemap(directorthrows) MyNS::ExceptionA, MyNS::ExceptionB, MyNS::Unexpected %{ - if (Swig::ExceptionMatches(jenv, $error, "$packagepath/$javaclassname")) + if (Swig::ExceptionMatches(jenv, $error, "$packagepath/$javaclassname")) { throw $1_type(Swig::JavaExceptionMessage(jenv, $error).message()); + } %} DECLARE_EXCEPTION(ExceptionA) @@ -4084,6 +4077,8 @@ DECLARE_EXCEPTION(Unexpected) %catches(MyNS::ExceptionA, MyNS::ExceptionB, MyNS::Unexpected) MyClass::meth2(); +%feature("director") MyClass; + %inline { class MyClass { public: @@ -4100,13 +4095,13 @@ DECLARE_EXCEPTION(Unexpected) In this case the three different "directorthrows" typemaps will be used to generate the three different exception handlers for meth1, meth2 and meth3. The generated -handlers will have "if" blocks for each exception type specified, in +handlers will have "if" blocks for each exception type, where the exception types are listed in either the exception specification or %catches feature.

Note that the "directorthrows" typemaps are important only if it is important for the exceptions passed through the C++ -layer to be mapped to distinct C++ exceptions. If director methods +layer need to be mapped to distinct C++ exceptions. If director methods are being called by C++ code that is itself wrapped in a SWIG generated Java wrapper and access is always through this wrapper, the default Swig::DirectorException class provides enough information diff --git a/Examples/test-suite/java/java_director_exception_feature_nspace_runme.java b/Examples/test-suite/java/java_director_exception_feature_nspace_runme.java index ea7da5c1a..884b04c6e 100644 --- a/Examples/test-suite/java/java_director_exception_feature_nspace_runme.java +++ b/Examples/test-suite/java/java_director_exception_feature_nspace_runme.java @@ -126,19 +126,28 @@ public class java_director_exception_feature_nspace_runme { try { b.genericpong(1); fail("No exception thrown in genericpong(1)"); } catch (MyJavaException1 e) { failif( ! java_director_exception_feature_nspace_Consts.GENERICPONGEXCP1.equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'"); + StackTraceElement[] st = e.getStackTrace(); + failif( st.length != 5, "Stack length is only " + st.length); + failif( ! st[0].toString().startsWith("java_director_exception_feature_nspace_MyFooDirectorImpl.genericpong(java_director_exception_feature_nspace_runme.java:"), "Incorrect top of stack: " + st[0]); } try { b.genericpong(2); fail("No exception thrown in genericpong(2)");} catch (java_director_exception_feature_nspace_NewCheckedException e) { failif( ! java_director_exception_feature_nspace_Consts.GENERICPONGEXCP2.equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'"); + StackTraceElement[] st = e.getStackTrace(); + failif( st.length != 5, "Stack length is only " + st.length); + failif( ! st[0].toString().startsWith("java_director_exception_feature_nspace_MyFooDirectorImpl.genericpong(java_director_exception_feature_nspace_runme.java:"), "Incorrect top of stack: " + st[0]); } try { b.genericpong(3); fail("No exception thrown in genericpong(3)");} catch (java_director_exception_feature_nspace_NewUncheckedException e) { failif( ! java_director_exception_feature_nspace_Consts.GENERICPONGEXCP3.equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'"); + StackTraceElement[] st = e.getStackTrace(); + failif( st.length != 5, "Stack length is only " + st.length); + failif( ! st[0].toString().startsWith("java_director_exception_feature_nspace_MyFooDirectorImpl.genericpong(java_director_exception_feature_nspace_runme.java:"), "Incorrect top of stack: " + st[0]); } try { b.genericpong(4); fail("No exception thrown in genericpong(4)");} catch (RuntimeException e) { - failif ( e.getClass() != RuntimeException.class, "Exception " + e + " is not exactly RumtimeException"); - failif( ! java_director_exception_feature_nspace_Consts.GENERICPONGEXCP4.equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'"); + failif ( e.getClass() != RuntimeException.class, "Exception " + e + " is not exactly RuntimeException"); + failif( ! "Unspecified DirectorException message".equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'"); } } catch (Exception e) { diff --git a/Examples/test-suite/java/java_director_exception_feature_runme.java b/Examples/test-suite/java/java_director_exception_feature_runme.java index 2e919c18a..d9763c992 100644 --- a/Examples/test-suite/java/java_director_exception_feature_runme.java +++ b/Examples/test-suite/java/java_director_exception_feature_runme.java @@ -127,19 +127,28 @@ public class java_director_exception_feature_runme { try { b.genericpong(1); fail("No exception thrown in genericpong(1)"); } catch (MyJavaException1 e) { failif( ! java_director_exception_feature_Consts.GENERICPONGEXCP1.equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'"); + StackTraceElement[] st = e.getStackTrace(); + failif( st.length != 5, "Stack length is only " + st.length); + failif( ! st[0].toString().startsWith("java_director_exception_feature_MyFooDirectorImpl.genericpong(java_director_exception_feature_runme.java:"), "Incorrect top of stack: " + st[0]); } try { b.genericpong(2); fail("No exception thrown in genericpong(2)");} catch (NewCheckedException e) { failif( ! java_director_exception_feature_Consts.GENERICPONGEXCP2.equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'"); + StackTraceElement[] st = e.getStackTrace(); + failif( st.length != 5, "Stack length is only " + st.length); + failif( ! st[0].toString().startsWith("java_director_exception_feature_MyFooDirectorImpl.genericpong(java_director_exception_feature_runme.java:"), "Incorrect top of stack: " + st[0]); } try { b.genericpong(3); fail("No exception thrown in genericpong(3)");} catch (NewUncheckedException e) { failif( ! java_director_exception_feature_Consts.GENERICPONGEXCP3.equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'"); + StackTraceElement[] st = e.getStackTrace(); + failif( st.length != 5, "Stack length is only " + st.length); + failif( ! st[0].toString().startsWith("java_director_exception_feature_MyFooDirectorImpl.genericpong(java_director_exception_feature_runme.java:"), "Incorrect top of stack: " + st[0]); } try { b.genericpong(4); fail("No exception thrown in genericpong(4)");} catch (RuntimeException e) { - failif ( e.getClass() != RuntimeException.class, "Exception " + e + " is not exactly RumtimeException"); - failif( ! java_director_exception_feature_Consts.GENERICPONGEXCP4.equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'"); + failif ( e.getClass() != RuntimeException.class, "Exception " + e + " is not exactly RuntimeException"); + failif( ! "Unspecified DirectorException message".equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'"); } } diff --git a/Examples/test-suite/java_director_exception_feature.i b/Examples/test-suite/java_director_exception_feature.i index a0b3b7261..0cd555875 100644 --- a/Examples/test-suite/java_director_exception_feature.i +++ b/Examples/test-suite/java_director_exception_feature.i @@ -42,7 +42,6 @@ %feature("director:except") MyNS::Foo::ping { jthrowable $error = jenv->ExceptionOccurred(); if ($error) { - jenv->ExceptionClear(); // clear java exception since mapping to c++ exception if (Swig::ExceptionMatches(jenv,$error,"$packagepath/MyJavaException1")) { throw 1; } else if (Swig::ExceptionMatches(jenv,$error,"$packagepath/MyJavaException2")) { @@ -71,7 +70,6 @@ %feature("director:except") MyNS::Foo::pong %{ jthrowable $error = jenv->ExceptionOccurred(); if ($error) { - jenv->ExceptionClear(); $directorthrowshandlers throw ::MyNS::Unexpected(Swig::JavaExceptionMessage(jenv,$error).message()); } @@ -121,7 +119,10 @@ %feature("director:except") MyNS::Foo::genericpong { jthrowable $error = jenv->ExceptionOccurred(); if ($error) { - jenv->ExceptionClear(); + if (Swig::ExceptionMatches(jenv,$error,"UnconstructableException")) { + // Purposefully test NULL + throw Swig::DirectorException(jenv, NULL); + } throw Swig::DirectorException(jenv,$error); } } diff --git a/Examples/test-suite/java_director_exception_feature_nspace.i b/Examples/test-suite/java_director_exception_feature_nspace.i index 3111538fc..aa8c64cc9 100644 --- a/Examples/test-suite/java_director_exception_feature_nspace.i +++ b/Examples/test-suite/java_director_exception_feature_nspace.i @@ -49,7 +49,6 @@ %feature("director:except") MyNS::Foo::ping { jthrowable $error = jenv->ExceptionOccurred(); if ($error) { - jenv->ExceptionClear(); // clear java exception since mapping to c++ exception if (Swig::ExceptionMatches(jenv,$error,"$packagepath/MyNS/MyJavaException1")) { throw 1; } else if (Swig::ExceptionMatches(jenv,$error,"$packagepath/MyNS/MyJavaException2")) { @@ -78,7 +77,6 @@ %feature("director:except") MyNS::Foo::pong %{ jthrowable $error = jenv->ExceptionOccurred(); if ($error) { - jenv->ExceptionClear(); $directorthrowshandlers throw ::MyNS::Unexpected(Swig::JavaExceptionMessage(jenv,$error).message()); } @@ -128,7 +126,10 @@ %feature("director:except") MyNS::Foo::genericpong { jthrowable $error = jenv->ExceptionOccurred(); if ($error) { - jenv->ExceptionClear(); + if (Swig::ExceptionMatches(jenv,$error,"java_director_exception_feature_nspace_UnconstructibleException")) { + // Purposefully test NULL + throw Swig::DirectorException(jenv, NULL); + } throw Swig::DirectorException(jenv,$error); } } diff --git a/Lib/java/director.swg b/Lib/java/director.swg index abde72286..e85ca376e 100644 --- a/Lib/java/director.swg +++ b/Lib/java/director.swg @@ -259,8 +259,8 @@ namespace Swig { JavaExceptionMessage(JNIEnv *jenv, jthrowable throwable) : message_(jenv, exceptionMessageFromThrowable(jenv, throwable)) { } - const char *message() const { - return message_.c_str("Could not get exception message in JavaExceptionMessage"); + const char *message(const char *null_string = "Could not get exception message in JavaExceptionMessage") const { + return message_.c_str(null_string); } private: @@ -294,10 +294,11 @@ namespace Swig { public: // Construct exception from a Java throwable - DirectorException(JNIEnv *jenv, jthrowable throwable) : classname_(0), msg_(0) { + DirectorException(JNIEnv *jenv, jthrowable throwable) : jenv_(jenv), throwable_(throwable), classname_(0), msg_(0) { // Call Java method Object.getClass().getName() to obtain the throwable's class name (delimited by '/') - if (throwable) { + if (jenv && throwable) { + jenv->ExceptionClear(); // Cannot invoke methods with any pending exceptions jclass throwclz = jenv->GetObjectClass(throwable); if (throwclz) { jclass clzclz = jenv->GetObjectClass(throwclz); @@ -318,11 +319,11 @@ namespace Swig { } JavaExceptionMessage exceptionmsg(jenv, throwable); - msg_ = copystr(exceptionmsg.message()); + msg_ = copystr(exceptionmsg.message(0)); } // More general constructor for handling as a java.lang.RuntimeException - DirectorException(const char *msg) : classname_(0), msg_(copystr(msg ? msg : "Unspecified DirectorException message")) { + DirectorException(const char *msg) : jenv_(0), throwable_(0), classname_(0), msg_(msg ? copystr(msg) : 0) { } ~DirectorException() throw() { @@ -331,28 +332,40 @@ namespace Swig { } const char *what() const throw() { - return msg_; + return msg_ ? msg_ : "Unspecified DirectorException message"; } // Reconstruct and raise/throw the Java Exception that caused the DirectorException // Note that any error in the JNI exception handling results in a Java RuntimeException void raiseJavaException(JNIEnv *jenv) const { if (jenv) { - jenv->ExceptionClear(); + if (jenv == jenv_ && throwable_) { + // Throw original exception if not already pending + jthrowable throwable = jenv->ExceptionOccurred(); + if (throwable && jenv->IsSameObject(throwable, throwable_) == JNI_FALSE) { + jenv->ExceptionClear(); + throwable = 0; + } + if (!throwable) + jenv->Throw(throwable_); + } else { + // Try and reconstruct original exception, but original stacktrace is not reconstructed + jenv->ExceptionClear(); - jmethodID ctorMethodID = 0; - jclass throwableclass = 0; - if (classname_) { - throwableclass = jenv->FindClass(classname_); - if (throwableclass) - ctorMethodID = jenv->GetMethodID(throwableclass, "", "(Ljava/lang/String;)V"); - } + jmethodID ctorMethodID = 0; + jclass throwableclass = 0; + if (classname_) { + throwableclass = jenv->FindClass(classname_); + if (throwableclass) + ctorMethodID = jenv->GetMethodID(throwableclass, "", "(Ljava/lang/String;)V"); + } - if (ctorMethodID) { - jenv->ThrowNew(throwableclass, what()); - } else { - SWIG_JavaThrowException(jenv, SWIG_JavaRuntimeException, what()); - } + if (ctorMethodID) { + jenv->ThrowNew(throwableclass, what()); + } else { + SWIG_JavaThrowException(jenv, SWIG_JavaRuntimeException, what()); + } + } } } @@ -380,6 +393,8 @@ namespace Swig { return target; } + JNIEnv *jenv_; + jthrowable throwable_; const char *classname_; const char *msg_; }; diff --git a/Source/Modules/java.cxx b/Source/Modules/java.cxx index 68f1bb273..f759aaf5d 100644 --- a/Source/Modules/java.cxx +++ b/Source/Modules/java.cxx @@ -4463,8 +4463,8 @@ public: if (!directorexcept) { directorexcept = NewString(""); Printf(directorexcept, "jthrowable $error = jenv->ExceptionOccurred();\n"); - Printf(directorexcept, "if ($error) {\n"); - Printf(directorexcept, " jenv->ExceptionClear();$directorthrowshandlers\n"); + Printf(directorexcept, "if ($error) {"); + Printf(directorexcept, "$directorthrowshandlers\n"); Printf(directorexcept, " Swig::DirectorException::raise(jenv, $error);\n"); Printf(directorexcept, "}\n"); } else {