diff options
34 files changed, 1479 insertions, 85 deletions
diff --git a/CHANGES.current b/CHANGES.current index db0a9899f..0f964b114 100644 --- a/CHANGES.current +++ b/CHANGES.current @@ -5,6 +5,20 @@ See the RELEASENOTES file for a summary of changes in each release. Version 3.0.0 (in progress) ============================ +2013-11-05: wsfulton + [Java] Fix some corner cases for the $packagepath/$javaclassname special variable substitution. + +2013-11-05: wsfulton + [Java] Apply patch #91 from Marvin Greenberg - Add director:except feature for improved + exception handling in director methods for Java. + +2013-10-17: wsfulton + [R] Fix SF #1340 - Visual Studio compile error in C++ wrappers due to #include <exception> + within extern "C" block. + +2013-10-17: wsfulton + [Python] Fix SF #1345 - Missing #include <stddef.h> for offsetof when using -builtin. + 2013-10-15: vadz Allow using \l, \L, \u, \U and \E in the substitution part of %(regex:/pattern/subst/) inside %rename to change the case of the text being replaced. diff --git a/Doc/Manual/Java.html b/Doc/Manual/Java.html index f3d8a1684..08c80c83a 100644 --- a/Doc/Manual/Java.html +++ b/Doc/Manual/Java.html @@ -87,6 +87,7 @@ <li><a href="#Java_directors_example">Simple directors example</a> <li><a href="#Java_directors_threading">Director threading issues</a> <li><a href="#Java_directors_performance">Director performance tuning</a> +<li><a href="#Java_exceptions_from_directors">Java Exceptions from Directors</a> </ul> <li><a href="#Java_allprotected">Accessing protected members</a> <li><a href="#Java_common_customization">Common customization features</a> @@ -3555,6 +3556,312 @@ However, if all director methods are expected to usually be overridden by Java s The disadvantage is that invocation of director methods from C++ when Java doesn't actually override the method will require an additional call up into Java and back to C++. As such, this option is only useful when overrides are extremely common and instantiation is frequent enough that its performance is critical. </p> +<H3><a name="Java_exceptions_from_directors"></a>24.5.7 Java exceptions from directors</H3> + +<p> +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 +<code>DirectorException</code> C++ exception. +SWIG 2.0 and earlier versions didn't provide any mechanism to handle the Java director method exceptions in C++. +</p> + +<p> +Converting Java exceptions into C++ exceptions can be done in two different ways using +the <code>director:except</code> <a href="Customization.html#Customization_features">feature</a>. +In the simplest approach, a code block is attached to each director method to +handle the mapping of Java exceptions into C++ exceptions. +</p> + +<div class="code"> +<pre> +%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")) + throw MyCppException(Swig::JavaExceptionMessage(jenv, $error).message()); + throw std::runtime_error("Unexpected exception thrown in MyClass::method"); + } +} + +class MyClass { + /** Throws either a std::out_of_range or MyCppException on error */ + void method(int x); +} +</pre> +</div> + +<p> +This approach allows a flexible mapping of Java exceptions thrown by director methods into +C++ exceptions expected by a C++ caller. There +need not be any C++ <em>exception specifications</em> on the C++ method. The +utility function <code>Swig::ExceptionMatches</code> +and class <code>Swig::JavaExceptionMessage</code> are provided to simplify +writing code for wrappers that use the <code>director:except</code> feature. The +function <code>Swig::ExceptionMatches</code> matches the type of the +<code>jthrowable</code> thrown against a <b>fully qualified</b> JNI style class +name, such as <code>"java/lang/IOError"</code>. If the throwable class is the same +type, or derives from the given type, <code>Swig::ExceptionMatches</code> will return true. Care must be taken to +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 <a href="#Java_namespaces">nspace + feature</a>. The special variable <code>$error</code> is expanded by SWIG into a unique variable name and +should be used for the +assignment of the exception that occurred. The special variable <code>$packagepath</code> is +replaced by the outer package provided for SWIG generation by the -package +option. The utility class <code>Swig::JavaExceptionMessage</code> is a holder +providing access to the message from the thrown Java exception. +The <code>message()</code> method returns the exception message as a <code>const char *</code>, +which is only valid during the lifetime of the holder. Any code using this message +needs to copy it, for example into a std::string or a newly constructed C++ exception. +</p> + +<p> +Using the above approach to +write handlers for a large number of methods will require +repetitive duplication of the <code>director:except</code> feature code. +To mitigate this, an alternative approach is provided via typemaps in a +fashion analagous to +the <a href="Typemaps.html#throws_typemap">"throws" typemap.</a> The +"throws" typemap provides an approach to automatically map all the C++ +exceptions listed in a method's defined exceptions (either from +a C++ <em>exception specification</em> or a <code>%catches</code> +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 <code>java.lang.IndexOutOfBoundsException</code> exception +to the typemap's type, that is <code>std::out_of_range</code>: + +<div class="code"> +<pre> +%typemap(directorthrows) std::out_of_range %{ + if (Swig::ExceptionMatches(jenv, $error, "java/lang/IndexOutOfBoundsException")) { + throw std::out_of_range(Swig::JavaExceptionMessage(jenv, $error).message()); + } +%} +</pre> +</div> + +<p> +The "directorthrows" typemap is then used in conjunction with the +<code>director:except</code> feature if the <code>$directorthrowshandlers</code> special variable +is used in the feature code. Consider the following, which also happens to be the default: +</p> + +<div class="code"> +<pre> +%feature("director:except") %{ + jthrowable $error = jenv->ExceptionOccurred(); + if ($error) { + jenv->ExceptionClear(); + $directorthrowshandlers + throw Swig::DirectorException(jenv, $error); + } +%} +</pre> +</div> + +<p>The code generated using the <code>director:except</code> feature +replaces the <code>$directorthrowshandlers</code> special variable with the code in +the "directorthrows" typemaps, for each and every exception defined for the method. +The possible exceptions can be defined either with a C++ exception +specification or <code>%catches</code> as described for the +<a href="Typemaps.html#throws_typemap">"throws" typemap</a>. +</p> + +<p> +Consider the following director method: +</p> + +<div class="code"> +<pre> + ... + virtual void doSomething(int index) throw (std::out_of_range); + ... +</pre> +</div> + +<p> +When combined with the default <code>director:except</code> feature and the "directorthrows" typemap above, +the resulting code generated in the director method after calling up to Java will be: +</p> + +<div class="code"> +<pre> +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()); + } + + throw Swig::DirectorException(jenv, swigerror); +} +</pre> +</div> + +<p><em> +Note: Beware of using exception specifications as the SWIG director methods +will be generated with the same exception specifications and if the +director method throws an exception that is not specified it is likely +to terminate your program. See the C++ standard for more details. +Using the %catches feature instead to define the handled exceptions does not suffer +this potential fate. +</em></p> + +<p>Because the default code generation maps any unhandled Java exceptions to +<code>Swig::DirectorException</code>, any director methods that have exception +specifications may cause program termination. To simply ignore +unexpected exceptions, the default handling can be changed with: + +<div class="code"> +<pre> +%feature("director:except") %{ + jthrowable $error = jenv->ExceptionOccurred(); + if ($error) { + jenv->ExceptionClear(); + $directorthrowshandlers + return $null; // exception is ignored + } +%} +</pre> +</div> +</p> + +<p>Alternatively an exception compatible with the existing director +method exception specifications can be thrown. Assuming that all +methods allow std::runtime_error to be thrown, +the <code>return $null;</code> could be changed to: +</p> + +<div class="code"> +<pre> + throw std::runtime_error(Swig::JavaExceptionMessage(jenv, $error).message()); +</pre> +</div> + +<p>In more complex situations, a separate <code>director:except</code> feature +may need to be attached to specific methods. +</p> + +<p>Below is a complete example demonstrating the use +of the "directorthrows" typemaps. In this example, a +generic "directorthrows" typemap is appropriate for all three exceptions - all +take single string constructors. If the exceptions had different constructors, +it would be neccessary to have separate typemaps for each exception type. + + +<!-- All the DEFINE_ and DECLARE_EXCEPTIONS CAN BE OMITTED to make + this more succinct. They are included to make this a complete + example interface that could be generated and built. --> +<div class="code"> +<pre> +%module(directors="1") example + +%{ + #include <string> + #include <stdexcept> +%} + +// Define exceptions in header section using std::runtime_error +%define DEFINE_EXCEPTION(NAME) +%{ + namespace MyNS { + struct NAME : public std::runtime_error { NAME(const std::string &what) : runtime_error(what) {} }; + } +%} +%enddef + +// Expose C++ exceptions as Java Exceptions by changing the Java base class and providing a getMessage() +%define DECLARE_EXCEPTION(NAME) +%typemap(javabase) MyNS::NAME "java.lang.Exception"; +%rename(getMessage) MyNS::NAME::what; +namespace MyNS { + struct NAME { + NAME(const std::string& what); + const char * what(); + }; +} +%enddef + +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")) + throw $1_type(Swig::JavaExceptionMessage(jenv, $error).message()); +%} + +DECLARE_EXCEPTION(ExceptionA) +DECLARE_EXCEPTION(ExceptionB) +DECLARE_EXCEPTION(Unexpected) + +%catches(MyNS::ExceptionA, MyNS::ExceptionB, MyNS::Unexpected) MyClass::meth2(); + +%inline { + class MyClass { + public: + virtual void meth1(int x) throw(MyNS::ExceptionA, MyNS::ExceptionB) = 0; + virtual void meth2() = 0; /* throws MyNS::ExceptionA, MyNS::ExceptionB, MyNS::Unexpected */ + virtual void meth3(float x) throw(MyNS::Unexpected) = 0; + virtual ~MyClass() {} + }; +} +</pre> +</div> + +<p> +In this case the three different "directorthrows" typemaps will be used +to generate the three different exception handlers for +<code>meth1</code>, <code>meth2</code> and <code>meth3</code>. The generated +handlers will have "if" blocks for each exception type specified, in +the exception specification or <code>%catches</code> feature. +</p> + +<p>Note that the "directorthrows" typemaps are important +only if it is important for the the exceptions passed through the C++ +layer 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 <code>Swig::DirectorException</code> class provides enough information +to reconstruct the original exception. In this case removing the +<code>$directorthrowshandlers</code> special variable from the +default <code>director:except</code> feature and simply always +throwing a <code>Swig::DirectorException</code> will achieve the desired result. +Along with this a generic exception feature is added to convert any +caught <code>Swig::DirectorException</code>s back into the underlying +Java exceptions via the <code>Swig::DirectorException::raiseJavaException</code> method, +as demonstrated with <code>%javaexception</code> below: +</p> + +<div class="code"> +<pre> +%javaexception("Exception") MyClass::myMethod %{ + try { + $action + } catch (Swig::DirectorException &e) { + // raise/throw the Java exception that originally caused the DirectorException + e.raiseJavaException(jenv); + return $null; + } +%} +</pre> +</div> + +<p> +See the <a href="#Java_exception_handling">Exception handling with %exception and %javaexception</a> +section for more on converting C++ exceptions to Java exceptions. +</p> <H2><a name="Java_allprotected"></a>24.6 Accessing protected members</H2> @@ -5367,7 +5674,7 @@ can be wrapped with the Java equivalent, that is, static inner proxy classes. </p> <p> -<b><tt>$jniinput, $javacall and $packagepath</tt></b><br> +<b><tt>$error, $jniinput, $javacall and $packagepath</tt></b><br> These special variables are used in the directors typemaps. See <a href="#Java_directors_typemaps">Director specific typemaps</a> for details. </p> @@ -5701,6 +6008,10 @@ is the package name passed from the SWIG command line and <code>$javaclassname</ If the <tt>-package</tt> commandline option is not used to specify the package, then '$packagepath/' will be removed from the resulting output JNI field descriptor. <b>Do not forget the terminating ';' for JNI field descriptors starting with 'L'.</b> If the ';' is left out, Java will generate a "method not found" runtime error. +Note that the <code>$packagepath</code> substitution always uses the path separator '/' when expanded. +The <code>$javaclassname</code> expansion can be confusing as it is normally expanded using the '.' separator. +However, <code>$javaclassname</code> is expanded using the path separator '/' in typemap's "descriptor" attribute +as well as in the "directorthrows" typemap. </p> </div> @@ -5796,6 +6107,40 @@ The target method is the method in the Java proxy class which overrides the virt </div> +<p><tt>%typemap(directorthrows)</tt></p> +<div class="indent"> + +<p> +Conversion of Java exceptions to C++ exceptions in director method's exception handling. +This typemap is expected to test the <tt>$error</tt> special variable for a matching Java exception +and if successful convert and throw it into a C++ exception given by the typemap's type. +The <code>$error</code> special variable is of type <code>jthrowable</code> and is +substituted with a unique variable name in the generated code. +</p> + +<p> +The example below converts a Java <code>java.lang.IndexOutOfBoundsException</code> exception +to the typemap's type, that is <code>std::out_of_range</code>: +</p> + +<div class="code"> +<pre> +%typemap(directorthrows) std::out_of_range %{ + if (Swig::ExceptionMatches(jenv, $error, "java/lang/IndexOutOfBoundsException")) { + throw std::out_of_range(Swig::JavaExceptionMessage(jenv, $error).message()); + } +%} +</pre> +</div> + +<p> +The utility function <code>Swig::ExceptionMatches</code> +and class <code>Swig::JavaExceptionMessage</code> are helpers available when using directors and are described +in the <a href="#Java_exceptions_from_directors">Java Exceptions from Directors</a> section. +</p> + +</div> + <p><tt>%typemap(javapackage)</tt></p> <div class="indent"> diff --git a/Doc/Manual/Warnings.html b/Doc/Manual/Warnings.html index aac415952..e0debe41c 100644 --- a/Doc/Manual/Warnings.html +++ b/Doc/Manual/Warnings.html @@ -499,6 +499,7 @@ example.i(4) : Syntax error in input. <li>474. Method <em>method</em> usage of the optimal attribute ignored in the out typemap as the following cannot be used to generate optimal code: <em>code</em> <li>475. Multiple calls to <em>method</em> might be generated due to optimal attribute usage in the out typemap. <li>476. Initialization using std::initializer_list. +<li>477. No directorthrows typemap defined for <em>type</em> </ul> diff --git a/Examples/csharp/variables/example.c b/Examples/csharp/variables/example.c index aa4ffe9b3..05e17c8c5 100644 --- a/Examples/csharp/variables/example.c +++ b/Examples/csharp/variables/example.c @@ -51,7 +51,7 @@ void print_vars() { printf("dvar = %g\n", dvar); printf("cvar = %c\n", cvar); printf("strvar = %s\n", strvar ? strvar : "(null)"); - printf("cstrvar = %s\n", cstrvar ? cstrvar : "(null)"); + printf("cstrvar = %s\n", cstrvar); printf("iptrvar = %p\n", iptrvar); printf("name = %s\n", name); printf("ptptr = %p (%d, %d)\n", ptptr, ptptr ? ptptr->x : 0, ptptr ? ptptr->y : 0); diff --git a/Examples/d/variables/example.c b/Examples/d/variables/example.c index 1bf9c120f..3b4e9f346 100644 --- a/Examples/d/variables/example.c +++ b/Examples/d/variables/example.c @@ -51,7 +51,7 @@ void print_vars() { printf("dvar = %g\n", dvar); printf("cvar = %c\n", cvar); printf("strvar = %s\n", strvar ? strvar : "(null)"); - printf("cstrvar = %s\n", cstrvar ? cstrvar : "(null)"); + printf("cstrvar = %s\n", cstrvar); printf("iptrvar = %p\n", iptrvar); printf("name = %s\n", name); printf("ptptr = %p (%d, %d)\n", ptptr, ptptr ? ptptr->x : 0, ptptr ? ptptr->y : 0); diff --git a/Examples/go/variables/example.c b/Examples/go/variables/example.c index aa4ffe9b3..05e17c8c5 100644 --- a/Examples/go/variables/example.c +++ b/Examples/go/variables/example.c @@ -51,7 +51,7 @@ void print_vars() { printf("dvar = %g\n", dvar); printf("cvar = %c\n", cvar); printf("strvar = %s\n", strvar ? strvar : "(null)"); - printf("cstrvar = %s\n", cstrvar ? cstrvar : "(null)"); + printf("cstrvar = %s\n", cstrvar); printf("iptrvar = %p\n", iptrvar); printf("name = %s\n", name); printf("ptptr = %p (%d, %d)\n", ptptr, ptptr ? ptptr->x : 0, ptptr ? ptptr->y : 0); diff --git a/Examples/java/variables/example.c b/Examples/java/variables/example.c index aa4ffe9b3..05e17c8c5 100644 --- a/Examples/java/variables/example.c +++ b/Examples/java/variables/example.c @@ -51,7 +51,7 @@ void print_vars() { printf("dvar = %g\n", dvar); printf("cvar = %c\n", cvar); printf("strvar = %s\n", strvar ? strvar : "(null)"); - printf("cstrvar = %s\n", cstrvar ? cstrvar : "(null)"); + printf("cstrvar = %s\n", cstrvar); printf("iptrvar = %p\n", iptrvar); printf("name = %s\n", name); printf("ptptr = %p (%d, %d)\n", ptptr, ptptr ? ptptr->x : 0, ptptr ? ptptr->y : 0); diff --git a/Examples/lua/variables/example.c b/Examples/lua/variables/example.c index aa4ffe9b3..05e17c8c5 100644 --- a/Examples/lua/variables/example.c +++ b/Examples/lua/variables/example.c @@ -51,7 +51,7 @@ void print_vars() { printf("dvar = %g\n", dvar); printf("cvar = %c\n", cvar); printf("strvar = %s\n", strvar ? strvar : "(null)"); - printf("cstrvar = %s\n", cstrvar ? cstrvar : "(null)"); + printf("cstrvar = %s\n", cstrvar); printf("iptrvar = %p\n", iptrvar); printf("name = %s\n", name); printf("ptptr = %p (%d, %d)\n", ptptr, ptptr ? ptptr->x : 0, ptptr ? ptptr->y : 0); diff --git a/Examples/octave/variables/example.c b/Examples/octave/variables/example.c index 15dcc1b8e..e2b72e0ea 100644 --- a/Examples/octave/variables/example.c +++ b/Examples/octave/variables/example.c @@ -51,7 +51,7 @@ void print_vars() { printf("dvar = %g\n", dvar); printf("cvar = %c\n", cvar); printf("strvar = %s\n", strvar ? strvar : "(null)"); - printf("cstrvar = %s\n", cstrvar ? cstrvar : "(null)"); + printf("cstrvar = %s\n", cstrvar); printf("iptrvar = %p\n", iptrvar); printf("name = %s\n", name); printf("ptptr = %p (%d, %d)\n", ptptr, ptptr ? ptptr->x : 0, ptptr ? ptptr->y : 0); diff --git a/Examples/perl5/variables/example.c b/Examples/perl5/variables/example.c index aa4ffe9b3..05e17c8c5 100644 --- a/Examples/perl5/variables/example.c +++ b/Examples/perl5/variables/example.c @@ -51,7 +51,7 @@ void print_vars() { printf("dvar = %g\n", dvar); printf("cvar = %c\n", cvar); printf("strvar = %s\n", strvar ? strvar : "(null)"); - printf("cstrvar = %s\n", cstrvar ? cstrvar : "(null)"); + printf("cstrvar = %s\n", cstrvar); printf("iptrvar = %p\n", iptrvar); printf("name = %s\n", name); printf("ptptr = %p (%d, %d)\n", ptptr, ptptr ? ptptr->x : 0, ptptr ? ptptr->y : 0); diff --git a/Examples/php/variables/example.c b/Examples/php/variables/example.c index 3114c7c5f..b21dee32d 100644 --- a/Examples/php/variables/example.c +++ b/Examples/php/variables/example.c @@ -51,7 +51,7 @@ void print_vars() { printf("dvar = %g\n", dvar); printf("cvar = %c\n", cvar); printf("strvar = %s\n", strvar ? strvar : "(null)"); - printf("cstrvar = %s\n", cstrvar ? cstrvar : "(null)"); + printf("cstrvar = %s\n", cstrvar); printf("iptrvar = %p\n", iptrvar); printf("name = %c%c%c%c%c\n", name[0],name[1],name[2],name[3],name[4]); printf("ptptr = %p %s\n", ptptr, Point_print( ptptr ) ); diff --git a/Examples/python/variables/example.c b/Examples/python/variables/example.c index aa4ffe9b3..05e17c8c5 100644 --- a/Examples/python/variables/example.c +++ b/Examples/python/variables/example.c @@ -51,7 +51,7 @@ void print_vars() { printf("dvar = %g\n", dvar); printf("cvar = %c\n", cvar); printf("strvar = %s\n", strvar ? strvar : "(null)"); - printf("cstrvar = %s\n", cstrvar ? cstrvar : "(null)"); + printf("cstrvar = %s\n", cstrvar); printf("iptrvar = %p\n", iptrvar); printf("name = %s\n", name); printf("ptptr = %p (%d, %d)\n", ptptr, ptptr ? ptptr->x : 0, ptptr ? ptptr->y : 0); diff --git a/Examples/ruby/variables/example.c b/Examples/ruby/variables/example.c index aa4ffe9b3..05e17c8c5 100644 --- a/Examples/ruby/variables/example.c +++ b/Examples/ruby/variables/example.c @@ -51,7 +51,7 @@ void print_vars() { printf("dvar = %g\n", dvar); printf("cvar = %c\n", cvar); printf("strvar = %s\n", strvar ? strvar : "(null)"); - printf("cstrvar = %s\n", cstrvar ? cstrvar : "(null)"); + printf("cstrvar = %s\n", cstrvar); printf("iptrvar = %p\n", iptrvar); printf("name = %s\n", name); printf("ptptr = %p (%d, %d)\n", ptptr, ptptr ? ptptr->x : 0, ptptr ? ptptr->y : 0); diff --git a/Examples/tcl/variables/example.c b/Examples/tcl/variables/example.c index aa4ffe9b3..05e17c8c5 100644 --- a/Examples/tcl/variables/example.c +++ b/Examples/tcl/variables/example.c @@ -51,7 +51,7 @@ void print_vars() { printf("dvar = %g\n", dvar); printf("cvar = %c\n", cvar); printf("strvar = %s\n", strvar ? strvar : "(null)"); - printf("cstrvar = %s\n", cstrvar ? cstrvar : "(null)"); + printf("cstrvar = %s\n", cstrvar); printf("iptrvar = %p\n", iptrvar); printf("name = %s\n", name); printf("ptptr = %p (%d, %d)\n", ptptr, ptptr ? ptptr->x : 0, ptptr ? ptptr->y : 0); diff --git a/Examples/test-suite/director_exception.i b/Examples/test-suite/director_exception.i index de0ef3343..3fd3e563c 100644 --- a/Examples/test-suite/director_exception.i +++ b/Examples/test-suite/director_exception.i @@ -41,6 +41,29 @@ class DirectorMethodException: public Swig::DirectorException {}; #endif +#ifdef SWIGJAVA + +// Default for director exception warns about unmapped exceptions now in java +// Suppress warnings for this older test +// %warnfilter(476) Bar; + +// Default for java is to throw Swig::DirectorException if no +// direct:except feature. Since methods below have exception specification +// cannot throw director exception. + +// Change back to old 2.0 default behavior + +%feature("director:except") { + jthrowable $error = jenv->ExceptionOccurred(); + if ($error) { + // Dont clear exception, still be active when return to java execution + // Essentially ignore exception occurred -- old behavior. + return $null; + } +} + +#endif + #ifdef SWIGRUBY %feature("director:except") { diff --git a/Examples/test-suite/java/Makefile.in b/Examples/test-suite/java/Makefile.in index e4f3c6b58..1bd4b0261 100644 --- a/Examples/test-suite/java/Makefile.in +++ b/Examples/test-suite/java/Makefile.in @@ -24,6 +24,8 @@ CPP_TEST_CASES = \ java_constants \ java_director \ java_director_assumeoverride \ + java_director_exception_feature \ + java_director_exception_feature_nspace \ java_enums \ java_jnitypes \ java_lib_arrays_dimensionless \ @@ -46,6 +48,7 @@ SWIGOPT += $(JAVA_PACKAGEOPT) # Custom tests - tests with additional commandline options java_nspacewithoutpackage.%: JAVA_PACKAGEOPT = +java_director_exception_feature_nspace.%: JAVA_PACKAGE = $*Package nspace.%: JAVA_PACKAGE = $*Package nspace_extend.%: JAVA_PACKAGE = $*Package director_nspace.%: JAVA_PACKAGE = $*Package 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 new file mode 100644 index 000000000..ea7da5c1a --- /dev/null +++ b/Examples/test-suite/java/java_director_exception_feature_nspace_runme.java @@ -0,0 +1,150 @@ +import java_director_exception_feature_nspacePackage.*; +import java_director_exception_feature_nspacePackage.MyNS.*; + +class java_director_exception_feature_nspace_Consts { + public static final String PINGEXCP1 = "Ping MyJavaException1"; // should get translated through an int on ping + public static final String PINGEXCP2 = "Ping MyJavaException2"; + + public static final String PONGEXCP1 = "Pong MyJavaException1"; + public static final String PONGEXCP2 = "Pong MyJavaException2"; + public static final String PONGUNEXPECTED = "Pong MyJavaUnexpected"; + public static final String TRANSLATED_NPE = "Pong Translated NPE"; + + public static final String GENERICPONGEXCP1 = "GenericPong Wrapped MyJavaException1"; + public static final String GENERICPONGEXCP2 = "GenericPong New Checked Exception"; + public static final String GENERICPONGEXCP3 = "GenericPong New Unchecked Exception"; + public static final String GENERICPONGEXCP4 = "GenericPong New Exception Without String ctor"; +} + +// an exception not mentioned or wrapped by the swig interface, +// to reconstruct using generic DirectorException handling +class java_director_exception_feature_nspace_NewCheckedException extends Exception { + public java_director_exception_feature_nspace_NewCheckedException(String s) { + super(s); + } +} + +// an exception not mentioned or wrapped by the swig interface, +// to reconstruct using generic DirectorException handling +class java_director_exception_feature_nspace_NewUncheckedException extends RuntimeException { + public java_director_exception_feature_nspace_NewUncheckedException(String s) { + super(s); + } +} + +// an exception not constructible from a string, +// to test DirectorException fallback reconstruction +class java_director_exception_feature_nspace_UnconstructibleException extends Exception { + private int extrastate; + public java_director_exception_feature_nspace_UnconstructibleException(int a, String s) { + super(s); + extrastate = a; + } +} + +class java_director_exception_feature_nspace_MyFooDirectorImpl extends Foo { + + public java_director_exception_feature_nspace_MyFooDirectorImpl() { }; + + @Override + public String ping(int excp) throws MyJavaException1, MyJavaException2 { + if (excp == 1) throw new MyJavaException1(java_director_exception_feature_nspace_Consts.PINGEXCP1); + if (excp == 2) throw new MyJavaException2(java_director_exception_feature_nspace_Consts.PINGEXCP2); + return "Ping director returned"; + } + @Override + public String pong(int excp) throws MyJavaException1, MyJavaException2, MyJavaUnexpected { + if (excp == 1) throw new MyJavaException1(java_director_exception_feature_nspace_Consts.PONGEXCP1); + if (excp == 2) throw new MyJavaException2(java_director_exception_feature_nspace_Consts.PONGEXCP2); + if (excp == 3) throw new MyJavaUnexpected(java_director_exception_feature_nspace_Consts.PONGUNEXPECTED); + if (excp == 4) throw new java.lang.NullPointerException(java_director_exception_feature_nspace_Consts.TRANSLATED_NPE); // should be translated to ::Unexpected + return "Pong director returned"; + } + + @Override + public String genericpong(int excp) throws MyJavaException1, java_director_exception_feature_nspace_NewCheckedException, java_director_exception_feature_nspace_UnconstructibleException { + if (excp == 1) + throw new MyJavaException1(java_director_exception_feature_nspace_Consts.GENERICPONGEXCP1); + if (excp == 2) + throw new java_director_exception_feature_nspace_NewCheckedException(java_director_exception_feature_nspace_Consts.GENERICPONGEXCP2); + if (excp == 3) + throw new java_director_exception_feature_nspace_NewUncheckedException(java_director_exception_feature_nspace_Consts.GENERICPONGEXCP3); + if (excp == 4) + throw new java_director_exception_feature_nspace_UnconstructibleException(1, java_director_exception_feature_nspace_Consts.GENERICPONGEXCP4); + return "GenericPong director returned"; + } +} + +public class java_director_exception_feature_nspace_runme { + + static { + try { + System.loadLibrary("java_director_exception_feature_nspace"); + } catch (UnsatisfiedLinkError e) { + System.err.println("Native code library failed to load. See the chapter on Dynamic Linking Problems in the SWIG Java documentation for help.\n" + e); + System.exit(1); + } + } + + public static void fail(String msg) { + System.err.println(msg); System.exit(1); + } + public static void failif(boolean cond, String msg) { + if (cond) fail(msg); + } + + + public static void main(String argv[]) { + + Bar b = new Bar(new java_director_exception_feature_nspace_MyFooDirectorImpl()); + try { + + try { b.ping(0); } catch (Exception e) + { fail("Exception should not have been thrown: " + e + " from ping(0)"); } + try { b.ping(1); fail("No exception thrown in ping(1)"); } catch (MyJavaException1 e) + // Should say "Threw some integer", see java_director_exception_feature.i Foo::ping throws a "1" + { failif( ! "Threw some integer".equals(e.getMessage()), "Ping exception not translated through int: '" + e.getMessage() + "'"); } + try { b.ping(2); fail("No exception thrown in ping(2)"); } catch (MyJavaException2 e) + { failif( ! java_director_exception_feature_nspace_Consts.PINGEXCP2.equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'"); } + + try { b.pong(0); } catch (Exception e) + { fail("Exception should not have been thrown: " + e + " from pong(0)"); } + try { b.pong(1); fail("No exception thrown in pong(1)"); } catch (MyJavaException1 e) + { failif( ! java_director_exception_feature_nspace_Consts.PONGEXCP1.equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'"); } + try { b.pong(2); fail("No exception thrown in pong(2)");} catch (MyJavaException2 e) + { failif( ! java_director_exception_feature_nspace_Consts.PONGEXCP2.equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'"); } + try { b.pong(3); fail("No exception thrown in pong(3)");} catch (MyJavaUnexpected e) + { failif( ! java_director_exception_feature_nspace_Consts.PONGUNEXPECTED.equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'"); } + try { b.pong(4); fail("No exception thrown in pong(4)"); } catch (MyJavaUnexpected e) + { failif( ! java_director_exception_feature_nspace_Consts.TRANSLATED_NPE.equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'"); } + + + try { b.genericpong(0); } + catch (Exception e) { + fail("Exception should not have been thrown: " + e + " from genericpong(0)"); + } + 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() + "'"); + } + 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() + "'"); + } + 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() + "'"); + } + 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() + "'"); + } + } + catch (Exception e) { + e.printStackTrace(); + fail("Unexpected exception thrown or exception not mapped properly"); + } + + } +} diff --git a/Examples/test-suite/java/java_director_exception_feature_runme.java b/Examples/test-suite/java/java_director_exception_feature_runme.java new file mode 100644 index 000000000..2e919c18a --- /dev/null +++ b/Examples/test-suite/java/java_director_exception_feature_runme.java @@ -0,0 +1,152 @@ + +import java_director_exception_feature.*; + +class java_director_exception_feature_Consts { + public static final String PINGEXCP1 = "Ping MyJavaException1"; // should get translated through an int on ping + public static final String PINGEXCP2 = "Ping MyJavaException2"; + + public static final String PONGEXCP1 = "Pong MyJavaException1"; + public static final String PONGEXCP2 = "Pong MyJavaException2"; + public static final String PONGUNEXPECTED = "Pong MyJavaUnexpected"; + public static final String TRANSLATED_NPE = "Pong Translated NPE"; + + public static final String GENERICPONGEXCP1 = "GenericPong Wrapped MyJavaException1"; + public static final String GENERICPONGEXCP2 = "GenericPong New Checked Exception"; + public static final String GENERICPONGEXCP3 = "GenericPong New Unchecked Exception"; + public static final String GENERICPONGEXCP4 = "GenericPong New Exception Without String ctor"; + +} + +// an exception not mentioned or wrapped by the swig interface, +// to reconstruct using generic DirectorException handling +class NewCheckedException extends Exception { + public NewCheckedException(String s) { + super(s); + } +} + +// an exception not mentioned or wrapped by the swig interface, +// to reconstruct using generic DirectorException handling +class NewUncheckedException extends RuntimeException { + public NewUncheckedException(String s) { + super(s); + } +} + +// an exception not constructable from a string, +// to test DirectorException fallback reconstruction +class UnconstructableException extends Exception { + private int extrastate; + public UnconstructableException(int a, String s) { + super(s); + extrastate = a; + } +} + +class java_director_exception_feature_MyFooDirectorImpl extends Foo { + + public java_director_exception_feature_MyFooDirectorImpl() { }; + + @Override + public String ping(int excp) throws MyJavaException1, MyJavaException2 { + if (excp == 1) throw new MyJavaException1(java_director_exception_feature_Consts.PINGEXCP1); + if (excp == 2) throw new MyJavaException2(java_director_exception_feature_Consts.PINGEXCP2); + return "Ping director returned"; + } + @Override + public String pong(int excp) throws MyJavaException1, MyJavaException2, MyJavaUnexpected { + if (excp == 1) throw new MyJavaException1(java_director_exception_feature_Consts.PONGEXCP1); + if (excp == 2) throw new MyJavaException2(java_director_exception_feature_Consts.PONGEXCP2); + if (excp == 3) throw new MyJavaUnexpected(java_director_exception_feature_Consts.PONGUNEXPECTED); + if (excp == 4) throw new java.lang.NullPointerException(java_director_exception_feature_Consts.TRANSLATED_NPE); // should be translated to ::Unexpected + return "Pong director returned"; + } + + @Override + public String genericpong(int excp) throws MyJavaException1, NewCheckedException, UnconstructableException { + if (excp == 1) + throw new MyJavaException1(java_director_exception_feature_Consts.GENERICPONGEXCP1); + if (excp == 2) + throw new NewCheckedException(java_director_exception_feature_Consts.GENERICPONGEXCP2); + if (excp == 3) + throw new NewUncheckedException(java_director_exception_feature_Consts.GENERICPONGEXCP3); + if (excp == 4) + throw new UnconstructableException(1, java_director_exception_feature_Consts.GENERICPONGEXCP4); + return "GenericPong director returned"; + } +} + +public class java_director_exception_feature_runme { + + static { + try { + System.loadLibrary("java_director_exception_feature"); + } catch (UnsatisfiedLinkError e) { + System.err.println("Native code library failed to load. See the chapter on Dynamic Linking Problems in the SWIG Java documentation for help.\n" + e); + System.exit(1); + } + } + + public static void fail(String msg) { + System.err.println(msg); System.exit(1); + } + public static void failif(boolean cond, String msg) { + if (cond) fail(msg); + } + + + public static void main(String argv[]) { + + Bar b = new Bar(new java_director_exception_feature_MyFooDirectorImpl()); + try { + + try { b.ping(0); } catch (Exception e) + { fail("Exception should not have been thrown: " + e + " from ping(0)"); } + try { b.ping(1); fail("No exception thrown in ping(1)"); } catch (MyJavaException1 e) + // Should say "Threw some integer", see java_director_exception_feature.i Foo::ping throws a "1" + { failif( ! "Threw some integer".equals(e.getMessage()), "Ping exception not translated through int: '" + e.getMessage() + "'"); } + try { b.ping(2); fail("No exception thrown in ping(2)"); } catch (MyJavaException2 e) + { failif( ! java_director_exception_feature_Consts.PINGEXCP2.equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'"); } + + try { b.pong(0); } catch (Exception e) + { fail("Exception should not have been thrown: " + e + " from pong(0)"); } + try { b.pong(1); fail("No exception thrown in pong(1)"); } catch (MyJavaException1 e) + { failif( ! java_director_exception_feature_Consts.PONGEXCP1.equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'"); } + try { b.pong(2); fail("No exception thrown in pong(2)");} catch (MyJavaException2 e) + { failif( ! java_director_exception_feature_Consts.PONGEXCP2.equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'"); } + try { b.pong(3); fail("No exception thrown in pong(3)");} catch (MyJavaUnexpected e) + { failif( ! java_director_exception_feature_Consts.PONGUNEXPECTED.equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'"); } + try { b.pong(4); fail("No exception thrown in pong(4)"); } catch (MyJavaUnexpected e) + { failif( ! java_director_exception_feature_Consts.TRANSLATED_NPE.equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'"); } + + + try { b.genericpong(0); } + catch (Exception e) { + fail("Exception should not have been thrown: " + e + " from genericpong(0)"); + } + 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() + "'"); + } + 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() + "'"); + } + 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() + "'"); + } + 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() + "'"); + } + + } + catch (Exception e) { + e.printStackTrace(); + fail("Unexpected exception thrown or exception not mapped properly"); + } + + } +} diff --git a/Examples/test-suite/java_director_exception_feature.i b/Examples/test-suite/java_director_exception_feature.i new file mode 100644 index 000000000..1552454dc --- /dev/null +++ b/Examples/test-suite/java_director_exception_feature.i @@ -0,0 +1,212 @@ +%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 + +#include <string> +%} + +%include <std_string.i> + +// DEFINE exceptions in header section using std::runtime_error +%{ + #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) { + 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")) { + 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) { + jenv->ExceptionClear(); + $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 %{ + jclass excpcls = jenv->FindClass("java_director_exception_feature/MyJavaException1"); + if (excpcls) { + jenv->ThrowNew(excpcls, $1.what()); + } + return $null; +%} + +%typemap(throws,throws="MyJavaException1") int %{ + 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) { + jenv->ExceptionClear(); + 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) {} +}; + +// 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; +}; + +} +%} diff --git a/Examples/test-suite/java_director_exception_feature_nspace.i b/Examples/test-suite/java_director_exception_feature_nspace.i new file mode 100644 index 000000000..aea362905 --- /dev/null +++ b/Examples/test-suite/java_director_exception_feature_nspace.i @@ -0,0 +1,219 @@ +%module(directors="1") java_director_exception_feature_nspace + +%include <std_except.i> + +%nspace; // turn namespace feature on for everything. + +#define PACKAGEDOT "java_director_exception_feature_nspacePackage." +#define PACKAGESLASH "java_director_exception_feature_nspacePackage/" +%{ +#define PACKAGEDOT "java_director_exception_feature_nspacePackage." +#define PACKAGESLASH "java_director_exception_feature_nspacePackage/" +%} + +%{ +#if defined(_MSC_VER) + #pragma warning(disable: 4290) // C++ exception specification ignored except to indicate a function is not __declspec(nothrow) +#endif + +#include <string> +%} + +%include <std_string.i> + +// DEFINE exceptions in header section using std::runtime_error +%{ + #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) { + 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")) { + 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) { + jenv->ExceptionClear(); + $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=PACKAGEDOT"MyNS.MyJavaException1") MyNS::Exception1 %{ + jclass excpcls = jenv->FindClass(PACKAGESLASH"MyNS/MyJavaException1"); + if (excpcls) { + jenv->ThrowNew(excpcls, $1.what()); + } + return $null; +%} + +%typemap(throws,throws=PACKAGEDOT"MyNS.MyJavaException1") int %{ + jclass excpcls = jenv->FindClass(PACKAGESLASH"MyNS/MyJavaException1"); + if (excpcls) { + jenv->ThrowNew(excpcls, "Threw some integer"); + } + return $null; +%} + +%typemap(throws,throws=PACKAGEDOT"MyNS.MyJavaException2") MyNS::Exception2 %{ + jclass excpcls = jenv->FindClass(PACKAGESLASH"MyNS/MyJavaException2"); + if (excpcls) { + jenv->ThrowNew(excpcls, $1.what()); + } + return $null; +%} + + +%typemap(throws,throws=PACKAGEDOT"MyNS.MyJavaUnexpected") MyNS::Unexpected %{ + jclass excpcls = jenv->FindClass(PACKAGESLASH"MyNS/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) { + jenv->ExceptionClear(); + 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; +}; + +// 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; +}; + +} +%} diff --git a/Lib/guile/guile_scm_run.swg b/Lib/guile/guile_scm_run.swg index 0ac51f919..2f8f3ae98 100644 --- a/Lib/guile/guile_scm_run.swg +++ b/Lib/guile/guile_scm_run.swg @@ -446,13 +446,8 @@ SWIG_Guile_Init () SWIGINTERN swig_module_info * SWIG_Guile_GetModule(void *SWIGUNUSEDPARM(clientdata)) { - SCM module; - SCM variable; - - module = SWIG_Guile_Init(); - - variable = scm_module_variable(module, - scm_from_locale_symbol("swig-type-list-address" SWIG_RUNTIME_VERSION SWIG_TYPE_TABLE_NAME)); + SCM module = SWIG_Guile_Init(); + SCM variable = scm_module_variable(module, scm_from_locale_symbol("swig-type-list-address" SWIG_RUNTIME_VERSION SWIG_TYPE_TABLE_NAME)); if (scm_is_false(variable)) { return NULL; } else { @@ -463,11 +458,7 @@ SWIG_Guile_GetModule(void *SWIGUNUSEDPARM(clientdata)) SWIGINTERN void SWIG_Guile_SetModule(swig_module_info *swig_module) { - SCM module; - SCM variable; - - module = SWIG_Guile_Init(); - + SCM module = SWIG_Guile_Init(); scm_module_define(module, scm_from_locale_symbol("swig-type-list-address" SWIG_RUNTIME_VERSION SWIG_TYPE_TABLE_NAME), scm_from_ulong((unsigned long) swig_module)); diff --git a/Lib/guile/std_vector.i b/Lib/guile/std_vector.i index 79c716b10..1c55239c1 100644 --- a/Lib/guile/std_vector.i +++ b/Lib/guile/std_vector.i @@ -306,7 +306,6 @@ namespace std { $1 = 1; } else { /* check the first element only */ - T* x; SCM o = scm_vector_ref($input,scm_from_ulong(0)); $1 = CHECK(o) ? 1 : 0; } @@ -315,7 +314,6 @@ namespace std { $1 = 1; } else if (scm_is_pair($input)) { /* check the first element only */ - T* x; SCM head = SCM_CAR($input); $1 = CHECK(head) ? 1 : 0; } else { @@ -335,7 +333,6 @@ namespace std { $1 = 1; } else { /* check the first element only */ - T* x; SCM o = scm_vector_ref($input,scm_from_ulong(0)); $1 = CHECK(o) ? 1 : 0; } @@ -344,7 +341,6 @@ namespace std { $1 = 1; } else if (scm_is_pair($input)) { /* check the first element only */ - T* x; SCM head = SCM_CAR($input); $1 = CHECK(head) ? 1 : 0; } else { diff --git a/Lib/java/director.swg b/Lib/java/director.swg index f32fda350..f9ddbeffe 100644 --- a/Lib/java/director.swg +++ b/Lib/java/director.swg @@ -7,11 +7,14 @@ #ifdef __cplusplus -#if defined(DEBUG_DIRECTOR_OWNED) +#if defined(DEBUG_DIRECTOR_OWNED) || defined(DEBUG_DIRECTOR_EXCEPTION) #include <iostream> #endif +#include <exception> + namespace Swig { + /* Java object wrapper */ class JObjectWrapper { public: @@ -74,8 +77,7 @@ namespace Swig { } /* Java proxy releases ownership of C++ object, C++ object is now - responsible for destruction (creates NewGlobalRef to pin Java - proxy) */ + responsible for destruction (creates NewGlobalRef to pin Java proxy) */ void java_change_ownership(JNIEnv *jenv, jobject jself, bool take_or_release) { if (take_or_release) { /* Java takes ownership of C++ object's lifetime. */ if (!weak_global_) { @@ -83,7 +85,8 @@ namespace Swig { jthis_ = jenv->NewWeakGlobalRef(jself); weak_global_ = true; } - } else { /* Java releases ownership of C++ object's lifetime */ + } else { + /* Java releases ownership of C++ object's lifetime */ if (weak_global_) { jenv->DeleteWeakGlobalRef((jweak)jthis_); jthis_ = jenv->NewGlobalRef(jself); @@ -191,8 +194,194 @@ namespace Swig { swig_self_.java_change_ownership(jenv, jself, take_or_release); } }; -} -#endif /* __cplusplus */ + // Utility classes and functions for exception handling. + + // Simple holder for a Java string during exception handling, providing access to a c-style string + class JavaString { + public: + JavaString(JNIEnv *jenv, jstring jstr) : jenv_(jenv), jstr_(jstr), cstr_(0) { + if (jenv_ && jstr_) + cstr_ = (const char *) jenv_->GetStringUTFChars(jstr_, NULL); + } + + ~JavaString() { + if (jenv_ && jstr_ && cstr_) + jenv_->ReleaseStringUTFChars(jstr_, cstr_); + } + + const char *c_str(const char *null_string = "null JavaString") const { + return cstr_ ? cstr_ : null_string; + } + + private: + // non-copyable + JavaString(const JavaString &); + JavaString &operator=(const JavaString &); + + JNIEnv *jenv_; + jstring jstr_; + const char *cstr_; + }; + + // Helper class to extract the exception message from a Java throwable + class JavaExceptionMessage { + public: + 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"); + } + + private: + // non-copyable + JavaExceptionMessage(const JavaExceptionMessage &); + JavaExceptionMessage &operator=(const JavaExceptionMessage &); + + // Get exception message by calling Java method Throwable.getMessage() + static jstring exceptionMessageFromThrowable(JNIEnv *jenv, jthrowable throwable) { + jstring jmsg = NULL; + if (jenv && throwable) { + jenv->ExceptionClear(); // Cannot invoke methods with any pending exceptions + jclass throwclz = jenv->GetObjectClass(throwable); + if (throwclz) { + // All Throwable classes have a getMessage() method, so call it to extract the exception message + jmethodID getMessageMethodID = jenv->GetMethodID(throwclz, "getMessage", "()Ljava/lang/String;"); + if (getMessageMethodID) + jmsg = (jstring)jenv->CallObjectMethod(throwable, getMessageMethodID); + } + if (jmsg == NULL && jenv->ExceptionCheck()) + jenv->ExceptionClear(); + } + return jmsg; + } + + JavaString message_; + }; + + // 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) : classname_(0), msg_(0) { + + // Call Java method Object.getClass().getName() to obtain the throwable's class name (delimited by '/') + if (throwable) { + jclass throwclz = jenv->GetObjectClass(throwable); + if (throwclz) { + jclass clzclz = jenv->GetObjectClass(throwclz); + if (clzclz) { + jmethodID getNameMethodID = jenv->GetMethodID(clzclz, "getName", "()Ljava/lang/String;"); + if (getNameMethodID) { + jstring jstr_classname = (jstring)(jenv->CallObjectMethod(throwclz, getNameMethodID)); + // Copy strings, since there is no guarantee that jenv will be active when handled + if (jstr_classname) { + JavaString jsclassname(jenv, jstr_classname); + const char *classname = jsclassname.c_str(0); + if (classname) + classname_ = copypath(classname); + } + } + } + } + } + JavaExceptionMessage exceptionmsg(jenv, throwable); + msg_ = copystr(exceptionmsg.message()); + } + + // More general constructor for handling as a java.lang.RuntimeException + DirectorException(const char *msg) : classname_(0), msg_(copystr(msg ? msg : "Unspecified DirectorException message")) { + } + + ~DirectorException() throw() { + delete[] classname_; + delete[] msg_; + } + + const char *what() const throw() { + return msg_; + } + + // 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(); + + jmethodID ctorMethodID = 0; + jclass throwableclass = 0; + if (classname_) { + throwableclass = jenv->FindClass(classname_); + if (throwableclass) + ctorMethodID = jenv->GetMethodID(throwableclass, "<init>", "(Ljava/lang/String;)V"); + } + + if (ctorMethodID) { + jenv->ThrowNew(throwableclass, what()); + } else { + SWIG_JavaThrowException(jenv, SWIG_JavaRuntimeException, what()); + } + } + } + + private: + static char *copypath(const char *srcmsg) { + char *target = copystr(srcmsg); + for (char *c=target; *c; ++c) { + if ('.' == *c) + *c = '/'; + } + return target; + } + + static char *copystr(const char *srcmsg) { + char *target = 0; + if (srcmsg) { + int msglen = strlen(srcmsg) + 1; + target = new char[msglen]; + strncpy(target, srcmsg, msglen); + } + return target; + } + + const char *classname_; + const char *msg_; + }; + + // Helper method to determine if a Java throwable matches a particular Java class type + bool ExceptionMatches(JNIEnv *jenv, jthrowable throwable, const char *classname) { + bool matches = false; + + if (throwable && jenv && classname) { + // Exceptions need to be cleared for correct behavior. + // The caller of ExceptionMatches should restore pending exceptions if desired - + // the caller already has the throwable. + jenv->ExceptionClear(); + + jclass clz = jenv->FindClass(classname); + if (clz) { + jclass classclz = jenv->GetObjectClass(clz); + jmethodID isInstanceMethodID = jenv->GetMethodID(classclz, "isInstance", "(Ljava/lang/Object;)Z"); + if (isInstanceMethodID) { + matches = jenv->CallBooleanMethod(clz, isInstanceMethodID, throwable) != 0; + } + } + +#if defined(DEBUG_DIRECTOR_EXCEPTION) + if (jenv->ExceptionCheck()) { + // Typically occurs when an invalid classname argument is passed resulting in a ClassNotFoundException + JavaExceptionMessage exc(jenv, jenv->ExceptionOccurred()); + std::cout << "Error: ExceptionMatches: class '" << classname << "' : " << exc.message() << std::endl; + } +#endif + } + return matches; + } + +} + +#endif /* __cplusplus */ diff --git a/Lib/java/std_string.i b/Lib/java/std_string.i index f178e6d43..5ad7d30bc 100644 --- a/Lib/java/std_string.i +++ b/Lib/java/std_string.i @@ -38,7 +38,9 @@ class string; %typemap(directorout) string %{ if(!$input) { - SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null string"); + if (!jenv->ExceptionCheck()) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null string"); + } return $null; } const char *$1_pstr = (const char *)jenv->GetStringUTFChars($input, 0); diff --git a/Lib/python/pyinit.swg b/Lib/python/pyinit.swg index 6a6de0963..79df023de 100644 --- a/Lib/python/pyinit.swg +++ b/Lib/python/pyinit.swg @@ -4,6 +4,10 @@ %insert(init) "swiginit.swg" +#if defined(SWIGPYTHON_BUILTIN) +%fragment("<stddef.h>"); // For offsetof +#endif + %init %{ #ifdef __cplusplus diff --git a/Lib/r/rrun.swg b/Lib/r/rrun.swg index f8bc9f497..990443e23 100644 --- a/Lib/r/rrun.swg +++ b/Lib/r/rrun.swg @@ -1,5 +1,6 @@ #ifdef __cplusplus +#include <exception> extern "C" { #endif @@ -369,7 +370,6 @@ SWIG_R_ConvertPacked(SEXP obj, void *ptr, size_t sz, swig_type_info *ty) { } #ifdef __cplusplus -#include <exception> #define SWIG_exception_noreturn(code, msg) do { throw std::runtime_error(msg); } while(0) #else #define SWIG_exception_noreturn(code, msg) do { return result; } while(0) diff --git a/Lib/ruby/rubycontainer.swg b/Lib/ruby/rubycontainer.swg index d4eaa5f73..dd6389ce4 100644 --- a/Lib/ruby/rubycontainer.swg +++ b/Lib/ruby/rubycontainer.swg @@ -805,7 +805,6 @@ namespace swig rb_raise( rb_eTypeError, "not a valid index or range" ); } - VALUE r = Qnil; static ID id_end = rb_intern("end"); static ID id_start = rb_intern("begin"); static ID id_noend = rb_intern("exclude_end?"); diff --git a/Lib/ruby/rubyprimtypes.swg b/Lib/ruby/rubyprimtypes.swg index df72e97f4..aa4f7ad37 100644 --- a/Lib/ruby/rubyprimtypes.swg +++ b/Lib/ruby/rubyprimtypes.swg @@ -193,7 +193,7 @@ SWIG_AsVal_dec(unsigned long long)(VALUE obj, unsigned long long *val) } %fragment(SWIG_AsVal_frag(double),"header",fragment="SWIG_ruby_failed") { -%ruby_aux_method(double, NUM2DBL, NUM2DBL(obj)) +%ruby_aux_method(double, NUM2DBL, NUM2DBL(obj); (void)type) SWIGINTERN int SWIG_AsVal_dec(double)(VALUE obj, double *val) diff --git a/Lib/ruby/rubyrun.swg b/Lib/ruby/rubyrun.swg index e851b1801..c3e0b749b 100644 --- a/Lib/ruby/rubyrun.swg +++ b/Lib/ruby/rubyrun.swg @@ -151,14 +151,13 @@ SWIG_Ruby_InitRuntime(void) SWIGRUNTIME void SWIG_Ruby_define_class(swig_type_info *type) { - VALUE klass; char *klass_name = (char *) malloc(4 + strlen(type->name) + 1); sprintf(klass_name, "TYPE%s", type->name); if (NIL_P(_cSWIG_Pointer)) { _cSWIG_Pointer = rb_define_class_under(_mSWIG, "Pointer", rb_cObject); rb_undef_method(CLASS_OF(_cSWIG_Pointer), "new"); } - klass = rb_define_class_under(_mSWIG, klass_name, _cSWIG_Pointer); + rb_define_class_under(_mSWIG, klass_name, _cSWIG_Pointer); free((void *) klass_name); } diff --git a/Source/Include/swigwarn.h b/Source/Include/swigwarn.h index 4856d9e44..65ff70dd9 100644 --- a/Source/Include/swigwarn.h +++ b/Source/Include/swigwarn.h @@ -176,6 +176,7 @@ #define WARN_TYPEMAP_OUT_OPTIMAL_IGNORED 474 #define WARN_TYPEMAP_OUT_OPTIMAL_MULTIPLE 475 #define WARN_TYPEMAP_INITIALIZER_LIST 476 +#define WARN_TYPEMAP_DIRECTORTHROWS_UNDEF 477 /* -- Fragments -- */ #define WARN_FRAGMENT_NOT_FOUND 490 diff --git a/Source/Modules/guile.cxx b/Source/Modules/guile.cxx index e0a084583..3a82d67d8 100644 --- a/Source/Modules/guile.cxx +++ b/Source/Modules/guile.cxx @@ -24,7 +24,7 @@ Guile Options (available with -guile)\n\ -exportprimitive - Add the (export ...) code from scmstub into the\n\ GOOPS file.\n\ -goopsprefix <prefix> - Prepend <prefix> to all goops identifiers\n\ - -linkage <lstyle> - Use linkage protocol <lstyle> (default `simple')\n\ + -Linkage <lstyle> - Use linkage protocol <lstyle> (default `simple')\n\ Use `module' for native Guile module linking\n\ (requires Guile >= 1.5.0). Use `passive' for\n\ passive linking (no C-level module-handling code),\n\ @@ -1350,7 +1350,7 @@ public: Printv(f_header, tm, "\n", NIL); } else { // Create variable and assign it a value - Printf(f_header, "static %s = %s;\n", SwigType_lstr(nctype, var_name), rvalue); + Printf(f_header, "static %s = %s;\n", SwigType_str(type, var_name), rvalue); } { /* Hack alert: will cleanup later -- Dave */ diff --git a/Source/Modules/java.cxx b/Source/Modules/java.cxx index 24435ac30..6576ad544 100644 --- a/Source/Modules/java.cxx +++ b/Source/Modules/java.cxx @@ -192,28 +192,31 @@ public: * * Test to see if a type corresponds to something wrapped with a proxy class. * Return NULL if not otherwise the proxy class name, fully qualified with - * package name if the nspace feature is used. + * package name if the nspace feature is used, unless jnidescriptor is true as + * the package name is handled differently (unfortunately for legacy reasons). * ----------------------------------------------------------------------------- */ - String *getProxyName(SwigType *t) { + String *getProxyName(SwigType *t, bool jnidescriptor = false) { String *proxyname = NULL; if (proxy_flag) { Node *n = classLookup(t); if (n) { proxyname = Getattr(n, "proxyname"); - if (!proxyname) { + if (!proxyname || jnidescriptor) { String *nspace = Getattr(n, "sym:nspace"); String *symname = Getattr(n, "sym:name"); if (nspace) { - if (package) + if (package && !jnidescriptor) proxyname = NewStringf("%s.%s.%s", package, nspace, symname); else proxyname = NewStringf("%s.%s", nspace, symname); } else { proxyname = Copy(symname); } - Setattr(n, "proxyname", proxyname); - Delete(proxyname); + if (!jnidescriptor) { + Setattr(n, "proxyname", proxyname); // Cache it + Delete(proxyname); + } } } } @@ -2885,6 +2888,7 @@ public: * getEnumName() * * If jnidescriptor is set, inner class names are separated with '$' otherwise a '.' + * and the package is also not added to the name. * ----------------------------------------------------------------------------- */ String *getEnumName(SwigType *t, bool jnidescriptor) { @@ -2899,7 +2903,7 @@ public: String *scopename_prefix = Swig_scopename_prefix(Getattr(n, "name")); String *proxyname = 0; if (scopename_prefix) { - proxyname = getProxyName(scopename_prefix); + proxyname = getProxyName(scopename_prefix, jnidescriptor); } if (proxyname) { const char *class_separator = jnidescriptor ? "$" : "."; @@ -2908,7 +2912,7 @@ public: // global enum or enum in a namespace String *nspace = Getattr(n, "sym:nspace"); if (nspace) { - if (package) + if (package && !jnidescriptor) enumname = NewStringf("%s.%s.%s", package, nspace, symname); else enumname = NewStringf("%s.%s", nspace, symname); @@ -2916,8 +2920,8 @@ public: enumname = Copy(symname); } } - if (!jnidescriptor) { // not cached - Setattr(n, "enumname", enumname); + if (!jnidescriptor) { + Setattr(n, "enumname", enumname); // Cache it Delete(enumname); } Delete(scopename_prefix); @@ -2935,10 +2939,11 @@ public: * that SWIG knows about. Also substitutes enums with enum name. * Otherwise use the $descriptor name for the Java class name. Note that the $&javaclassname substitution * is the same as a $&descriptor substitution, ie one pointer added to descriptor name. + * Note that the path separator is a '.' unless jnidescriptor is set. * Inputs: * pt - parameter type * tm - typemap contents that might contain the special variable to be replaced - * jnidescriptor - if set, inner class names are separated with '$' otherwise a '.' + * jnidescriptor - if set, inner class names are separated with '$' otherwise a '/' is used for the path separator * Outputs: * tm - typemap contents complete with the special variable substitution * Return: @@ -2984,25 +2989,31 @@ public: * ----------------------------------------------------------------------------- */ void substituteClassnameSpecialVariable(SwigType *classnametype, String *tm, const char *classnamespecialvariable, bool jnidescriptor) { + String *replacementname; + if (SwigType_isenum(classnametype)) { String *enumname = getEnumName(classnametype, jnidescriptor); if (enumname) - Replaceall(tm, classnamespecialvariable, enumname); + replacementname = Copy(enumname); else - Replaceall(tm, classnamespecialvariable, NewStringf("int")); + replacementname = NewString("int"); } else { - String *classname = getProxyName(classnametype); + String *classname = getProxyName(classnametype, jnidescriptor); // getProxyName() works for pointers to classes too if (classname) { - Replaceall(tm, classnamespecialvariable, classname); // getProxyName() works for pointers to classes too - } else { // use $descriptor if SWIG does not know anything about this type. Note that any typedefs are resolved. - String *descriptor = NewStringf("SWIGTYPE%s", SwigType_manglestr(classnametype)); - Replaceall(tm, classnamespecialvariable, descriptor); + replacementname = Copy(classname); + } else { + // use $descriptor if SWIG does not know anything about this type. Note that any typedefs are resolved. + replacementname = NewStringf("SWIGTYPE%s", SwigType_manglestr(classnametype)); // Add to hash table so that the type wrapper classes can be created later - Setattr(swig_types_hash, descriptor, classnametype); - Delete(descriptor); + Setattr(swig_types_hash, replacementname, classnametype); } } + if (jnidescriptor) + Replaceall(replacementname,".","/"); + Replaceall(tm, classnamespecialvariable, replacementname); + + Delete(replacementname); } /* ----------------------------------------------------------------------------- @@ -3497,10 +3508,36 @@ public: // Delete(method_attr); } + /* ----------------------------------------------------------------------------- + * substitutePackagePath() + * + * Replace $packagepath using the javapackage typemap associated with passed + * parm or global package if p is 0. "$packagepath/" is replaced with "" if + * no package is set. Note that the path separator is a '/'. + * ----------------------------------------------------------------------------- */ + + void substitutePackagePath(String *text, Parm *p) { + String *pkg_path= 0; + + if (p) + pkg_path = Swig_typemap_lookup("javapackage", p, "", 0); + if (!pkg_path || Len(pkg_path) == 0) + pkg_path = Copy(package_path); + + if (Len(pkg_path) > 0) { + Replaceall(pkg_path, ".", "/"); + Replaceall(text, "$packagepath", pkg_path); + } else { + Replaceall(text, "$packagepath/", empty_string); + Replaceall(text, "$packagepath", empty_string); + } + Delete(pkg_path); + } + /* --------------------------------------------------------------- * Canonicalize the JNI field descriptor * - * Replace the $javapackage and $javaclassname family of special + * Replace the $packagepath and $javaclassname family of special * variables with the desired package and Java proxy name as * required in the JNI field descriptors. * @@ -3511,27 +3548,11 @@ public: * --------------------------------------------------------------- */ String *canonicalizeJNIDescriptor(String *descriptor_in, Parm *p) { - String *pkg_path = Swig_typemap_lookup("javapackage", p, "", 0); SwigType *type = Getattr(p, "type"); - - if (!pkg_path || Len(pkg_path) == 0) - pkg_path = package_path; - String *descriptor_out = Copy(descriptor_in); substituteClassname(type, descriptor_out, true); - - if (Len(pkg_path) > 0 && Strchr(descriptor_out, '.') == NULL) { - Replaceall(descriptor_out, "$packagepath", pkg_path); - } else { - Replaceall(descriptor_out, "$packagepath/", empty_string); - Replaceall(descriptor_out, "$packagepath", empty_string); - } - - Replaceall(descriptor_out, ".", "/"); - - if (pkg_path != package_path) - Delete(pkg_path); + substitutePackagePath(descriptor_out, p); return descriptor_out; } @@ -3928,17 +3949,33 @@ public: // Get any Java exception classes in the throws typemap ParmList *throw_parm_list = NULL; + // May need to add Java throws clause to director methods if %catches defined + // Get any Java exception classes in the throws typemap + ParmList *catches_list = Getattr(n, "catchlist"); + if (catches_list) { + Swig_typemap_attach_parms("throws", catches_list, 0); + Swig_typemap_attach_parms("directorthrows", catches_list, 0); + for (p = catches_list; p; p = nextSibling(p)) { + addThrows(n, "tmap:throws", p); + } + } + if ((throw_parm_list = Getattr(n, "throws")) || Getattr(n, "throw")) { int gencomma = 0; Append(w->def, " throw("); Append(declaration, " throw("); - if (throw_parm_list) + if (throw_parm_list) { Swig_typemap_attach_parms("throws", throw_parm_list, 0); + Swig_typemap_attach_parms("directorthrows", throw_parm_list, 0); + } for (p = throw_parm_list; p; p = nextSibling(p)) { if (Getattr(p, "tmap:throws")) { - addThrows(n, "tmap:throws", p); + // %catches replaces the specified exception specification + if (!catches_list) { + addThrows(n, "tmap:throws", p); + } if (gencomma++) { Append(w->def, ", "); @@ -4001,7 +4038,8 @@ public: Printf(w->code, "jenv->%s(Swig::jclass_%s, Swig::director_methids[%s], %s);\n", methop, imclass_name, methid, jupcall_args); - Printf(w->code, "if (jenv->ExceptionCheck() == JNI_TRUE) return $null;\n"); + // Generate code to handle any Java exception thrown by director delegation + directorExceptHandler(n, catches_list ? catches_list : throw_parm_list, w); if (!is_void) { String *jresult_str = NewString("jresult"); @@ -4042,7 +4080,8 @@ public: /* Terminate wrapper code */ Printf(w->code, "} else {\n"); - Printf(w->code, "SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"null upcall object\");\n"); + Printf(w->code, "SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"null upcall object in %s::%s \");\n", + SwigType_namestr(c_classname), SwigType_namestr(name)); Printf(w->code, "}\n"); Printf(w->code, "if (swigjobj) jenv->DeleteLocalRef(swigjobj);\n"); @@ -4099,6 +4138,61 @@ public: } /* ------------------------------------------------------------ + * directorExceptHandler() + * + * Emit code to map Java exceptions back to C++ exceptions when + * feature("director:except") is applied to a method node. + * This is generated after the Java method upcall. + * ------------------------------------------------------------ */ + + void directorExceptHandler(Node *n, ParmList *throw_parm_list, Wrapper *w) { + + String *directorexcept = Getattr(n, "feature:director:except"); + if (!directorexcept) { + directorexcept = NewString(""); + Printf(directorexcept, "jthrowable $error = jenv->ExceptionOccurred();\n"); + Printf(directorexcept, "if ($error) {\n"); + Printf(directorexcept, " jenv->ExceptionClear();$directorthrowshandlers\n"); + Printf(directorexcept, " throw Swig::DirectorException(jenv, $error);\n"); + Printf(directorexcept, "}\n"); + } else { + directorexcept = Copy(directorexcept); + } + + // Can explicitly disable director:except by setting to "" or "0" + if (Len(directorexcept) > 0 && Cmp(directorexcept, "0") != 0) { + + // Replace $packagepath + substitutePackagePath(directorexcept, 0); + + // Replace $directorthrowshandlers with any defined typemap handlers (or nothing) + if (Strstr(directorexcept, "$directorthrowshandlers")) { + String *directorthrowshandlers_code = NewString(""); + + for (Parm *p = throw_parm_list; p; p = nextSibling(p)) { + String *tm = Getattr(p, "tmap:directorthrows"); + + if (tm) { + // replace $packagepath/$javaclassname + String *directorthrows = canonicalizeJNIDescriptor(tm, p); + Printv(directorthrowshandlers_code, directorthrows, NIL); + Delete(directorthrows); + } else { + String *t = Getattr(p,"type"); + Swig_warning(WARN_TYPEMAP_DIRECTORTHROWS_UNDEF, Getfile(n), Getline(n), "No directorthrows typemap defined for %s\n", SwigType_str(t, 0)); + } + } + Replaceall(directorexcept, "$directorthrowshandlers", directorthrowshandlers_code); + Delete(directorthrowshandlers_code); + } + + Replaceall(directorexcept, "$error", "swigerror"); + Printf(w->code, " %s\n", directorexcept); + } + Delete(directorexcept); + } + + /* ------------------------------------------------------------ * directorPrefixArgs() * ------------------------------------------------------------ */ diff --git a/Source/Modules/php.cxx b/Source/Modules/php.cxx index 4ade67250..def917019 100644 --- a/Source/Modules/php.cxx +++ b/Source/Modules/php.cxx @@ -443,7 +443,6 @@ public: Append(s_header, " zval **args[2];\n"); Append(s_header, " swig_object_wrapper *value;\n"); Append(s_header, " int type;\n"); - Append(s_header, " int thisown;\n"); Append(s_header, "\n"); Append(s_header, " SWIG_ResetError();\n"); Append(s_header, " if(ZEND_NUM_ARGS() != 2 || zend_get_parameters_array_ex(2, args) != SUCCESS) {\n"); diff --git a/Source/Swig/stype.c b/Source/Swig/stype.c index 4b72bdaa2..506878799 100644 --- a/Source/Swig/stype.c +++ b/Source/Swig/stype.c @@ -131,6 +131,7 @@ SwigType *NewSwigType(int t) { break; case T_STRING: { SwigType *t = NewString("char"); + SwigType_add_qualifier(t, "const"); SwigType_add_pointer(t); return t; break; |