From 077bb0b04fdb644af65dc83973137cf5dbbb9d5a Mon Sep 17 00:00:00 2001 From: William S Fulton Date: Thu, 16 Nov 2017 20:03:58 +0000 Subject: [PATCH] Improved Java director exceptions documentation --- Doc/Manual/Java.html | 89 ++++++++++++++++++++++++++++++++-------- Doc/Manual/Typemaps.html | 17 +++++--- Lib/java/director.swg | 3 ++ 3 files changed, 86 insertions(+), 23 deletions(-) diff --git a/Doc/Manual/Java.html b/Doc/Manual/Java.html index 664ccb7d2..be8392411 100644 --- a/Doc/Manual/Java.html +++ b/Doc/Manual/Java.html @@ -3825,17 +3825,20 @@ The disadvantage is that invocation of director methods from C++ when Java doesn

With directors routing method calls to Java, and proxies routing them to C++, the handling of exceptions is an important concern. -The default behavior from SWIG 3.0 -onwards is to convert the thrown Java exception into a SWIG defined -DirectorException C++ exception. -SWIG 2.0 and earlier versions didn't provide any mechanism to handle the Java director method exceptions in C++. +The default behavior for Java exceptions thrown in a director method overridden in Java is +to convert the thrown Java exception into a SWIG defined +DirectorException C++ class exception in the C++ layer and then throw this C++ exception.

-Converting Java exceptions into C++ exceptions can be done in two different ways using +The conversion of Java exceptions into C++ exceptions can be customized in two different ways using the director:except feature. In the simplest approach, a code block is attached to each director method to handle the mapping of Java exceptions into C++ exceptions. +The example below converts a +java.lang.IndexOutOfBoundsException into a C++ std::out_of_range exception and converts a +user's Java MyJavaException into a C++ MyCppException exception. +If the Java exception doesn't match either of these, a fallback std::runtime_error C++ exception is thrown.

@@ -3858,6 +3861,61 @@ class MyClass {
+

+A few special variables are expanded from the director:except feature when generated. +The special variable $error is expanded by SWIG into a unique variable name and +should be used for the +assignment of the exception that occurred. The special variable $packagepath is +replaced by the outer package provided for SWIG generation by the -package option. +

+ +

+Utility functions/classes in director.swg are provided to aid the exception conversion as follows: +

+ +
+
+namespace Swig {
+
+  // Helper method to determine if a Java throwable matches a particular Java class type
+  bool ExceptionMatches(JNIEnv *jenv, jthrowable throwable, const char *classname);
+
+  // Helper class to extract the exception message from a Java throwable
+  class JavaExceptionMessage {
+  public:
+    JavaExceptionMessage(JNIEnv *jenv, jthrowable throwable);
+
+    // Return a C string of the exception message in the jthrowable passed in the constructor
+    // If no message is available, null_string is return instead
+    const char *message(const char *null_string = 
+                        "Could not get exception message in JavaExceptionMessage") const;
+  };
+
+  // C++ Exception class for handling Java exceptions thrown during a director method Java upcall
+  class DirectorException : public std::exception {
+  public:
+
+    // Construct exception from a Java throwable
+    DirectorException(JNIEnv *jenv, jthrowable throwable);
+
+    // More general constructor for handling as a java.lang.RuntimeException
+    DirectorException(const char *msg);
+
+    // Return exception message extracted from the Java throwable
+    const char *what() const throw();
+
+    // 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;
+
+    // Create and throw the DirectorException
+    static void raise(JNIEnv *jenv, jthrowable throwable);
+  };
+
+}
+
+
+

This approach allows a flexible mapping of Java exceptions thrown by director methods into C++ exceptions expected by a C++ caller. There @@ -3872,11 +3930,8 @@ type, or derives from the given type, Swig::ExceptionMatches will r provide the correct fully qualified name, since for wrapped exceptions the generated proxy class will have additional package qualification, depending on the '-package' argument and use of the nspace - feature. The special variable $error is expanded by SWIG into a unique variable name and -should be used for the -assignment of the exception that occurred. The special variable $packagepath is -replaced by the outer package provided for SWIG generation by the -package -option. The utility class Swig::JavaExceptionMessage is a holder + feature. +The utility class Swig::JavaExceptionMessage is a holder providing access to the message from the thrown Java exception. The message() method returns the exception message as a const char *, which is only valid during the lifetime of the holder. Any code using this message @@ -3884,20 +3939,20 @@ needs to copy it, for example into a std::string or a newly constructed C++ exce

-Using the above approach to +Using the above simple approach to write handlers for a large number of methods will require repetitive duplication of the director:except feature code. To mitigate this, an alternative approach is provided via typemaps in a fashion analagous to -the "throws" typemap. The -"throws" typemap provides an approach to automatically map all the C++ +the "throws" typemap. +The "throws" typemap provides the second approach to map all the C++ exceptions listed in a method's defined exceptions (either from a C++ exception specification or a %catches feature) into Java exceptions. The "directorthrows" typemap provides the inverse mapping and should contain code to convert a suitably matching Java exception into a C++ exception. The example below converts a Java java.lang.IndexOutOfBoundsException exception -to the typemap's type, that is std::out_of_range: +to the typemap's type, that is a std::out_of_range C++ exception:

@@ -3928,7 +3983,7 @@ is used in the feature code. Consider the following, which also happens to be th
 

-where Swig::DirectorException::raise is a helper method in the DirectorException class to throw a C++ exception (implemented in director.swg): +where Swig::DirectorException::raise is the helper method to throw a C++ Swig::DirectorException.

@@ -3948,7 +4003,7 @@ specification or %catches as described for the

-Consider the following director method: +Let's try all this together and consider the following director method:

@@ -3997,8 +4052,8 @@ unexpected exceptions, the default handling can be changed by adding: %feature("director:except") %{ jthrowable $error = jenv->ExceptionOccurred(); if ($error) { - jenv->ExceptionClear(); $directorthrowshandlers + jenv->ExceptionClear(); return $null; // exception is ignored } %} diff --git a/Doc/Manual/Typemaps.html b/Doc/Manual/Typemaps.html index ee807d118..371d0debd 100644 --- a/Doc/Manual/Typemaps.html +++ b/Doc/Manual/Typemaps.html @@ -2901,12 +2901,18 @@ For example: PyErr_SetString(PyExc_RuntimeError, $1); SWIG_fail; %} -void bar() throw (const char *); + +// Either an exception specification on the method + void bar() throw (const char *); + +// Or a %catches feature attached to the method + %catches(const char *) bar(); + void bar();

-As can be seen from the generated code below, SWIG generates an exception handler +As can be seen from the resulting generated code below, SWIG generates an exception handler with the catch block comprising the "throws" typemap content.

@@ -2915,8 +2921,7 @@ with the catch block comprising the "throws" typemap content. ... try { bar(); -} -catch(char const *_e) { +} catch(char const *_e) { PyErr_SetString(PyExc_RuntimeError, _e); SWIG_fail; } @@ -2925,8 +2930,8 @@ catch(char const *_e) {

-Note that if your methods do not have an exception specification yet they do throw exceptions, SWIG cannot know how to deal with them. -For a neat way to handle these, see the Exception handling with %exception section. +Note that if your methods do not have an exception specification but they do throw exceptions and you are not using %catches, SWIG cannot know how to deal with them. +Please also see the Exception handling with %exception section for another way to handle exceptions.

11.6 Some typemap examples

diff --git a/Lib/java/director.swg b/Lib/java/director.swg index e85ca376e..8226773dd 100644 --- a/Lib/java/director.swg +++ b/Lib/java/director.swg @@ -259,6 +259,8 @@ namespace Swig { JavaExceptionMessage(JNIEnv *jenv, jthrowable throwable) : message_(jenv, exceptionMessageFromThrowable(jenv, throwable)) { } + // Return a C string of the exception message in the jthrowable passed in the constructor + // If no message is available, null_string is return instead const char *message(const char *null_string = "Could not get exception message in JavaExceptionMessage") const { return message_.c_str(null_string); } @@ -369,6 +371,7 @@ namespace Swig { } } + // Create and throw the DirectorException static void raise(JNIEnv *jenv, jthrowable throwable) { throw DirectorException(jenv, throwable); }