Add feature director:except for improved director exception handling in Java
Closes #91
This commit is contained in:
parent
ec1d5a5be1
commit
6736e74127
12 changed files with 1469 additions and 51 deletions
|
|
@ -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,281 @@ 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. In Swig
|
||||
2.0, the director class methods ignored java exceptions that occurred
|
||||
during method calls dispatched to the Java director class and simply
|
||||
returned '$null' to the C++ caller. The default behavior now throws a
|
||||
Swig-defined <code>DirectorException</code> C++ exception. A facility
|
||||
is now provided to allow translation of thrown Java exceptions into
|
||||
C++ exceptions. This can be done in two different ways using
|
||||
the <code>%feature("director:except")</code> directive. 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>
|
||||
// All the rules to associate a feature with an element apply
|
||||
%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());
|
||||
else if (Swig::ExceptionMatches(jenv,$error,"$packagepath/MyJavaException"))
|
||||
throw MyCppException(Swig::JavaExceptionMessage(jenv,$error).message());
|
||||
else
|
||||
throw std::runtime_error("Unexpected exception thrown by MyClass::method");
|
||||
}
|
||||
}
|
||||
|
||||
class MyClass {
|
||||
void method(int x); /* on C++ side, may get std::runtime_error or MyCppException */
|
||||
}
|
||||
</pre>
|
||||
</div>
|
||||
<p>
|
||||
This approach allows mapping java exceptions thrown by director methods into
|
||||
C++ exceptions, to match the exceptions expected by a C++ caller. There
|
||||
need not be any exception specification on the method. This approach gives
|
||||
complete flexibility to map exceptions thrown by a java director
|
||||
implementation into desired C++ exceptions. The
|
||||
function <code>Swig::ExceptionMatches</code>
|
||||
and class <code>Swig::JavaExceptionMessage</code> are provided to simplify
|
||||
writing code for wrappers that use <code>director:except</code>feature. These simplify
|
||||
testing the type of the java exception and constructing C++ exceptions. 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, like <code>"java/lang/IOError"</code>. If the throwable class is the same
|
||||
type, or derives from the given type, it returns 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 <a href="#Java_namespaces">nspace
|
||||
feature</a>. The variable $error is simply a unique variable name to allow
|
||||
assignment of the exception that occurred. The variable $packagepath is
|
||||
replaced by the outer package provided for swig generation by the -package
|
||||
option. The class <code>Swig::JavaExceptionMessage</code> is a holder
|
||||
object giving access to the message from the thrown java exception.
|
||||
The message() 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>
|
||||
If many methods may throw different exceptions, using this approach to
|
||||
write handlers for a large number of methods will result in
|
||||
duplication of the code in the <code>director:except</code> feature
|
||||
code blocks, and will require separate feature definitions for every
|
||||
method. So an alternative approach is provided, using 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
|
||||
an <em>exception specification</em> or a <code>%catches</code>
|
||||
feature) into Java exceptions, for the generated proxy classes. To
|
||||
provide the inverse mapping, the <code>directorthrows</code> typemap
|
||||
is provided.
|
||||
|
||||
<p>Using directorthrows typemaps allows a
|
||||
generic <code>director:except</code> feature to be combined with
|
||||
method-specific handling to achieve the desired result. The
|
||||
default <code>director:except</code> feature, in combination
|
||||
with <code>directorthrows</code> typemaps generate exception mapping
|
||||
to C++ exceptions for all the exceptions defined for each method. The
|
||||
default definition is shown below.</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> with code that throws
|
||||
appropriate C++ exceptions from <code>directorthrows</code> typemaps
|
||||
for each exception defined for the method. Just as with
|
||||
the <a href="Typemaps.html#throws_typemap">"throws" typemap</a>, the
|
||||
possible exceptions may be defined either with an exception
|
||||
specification (<code> throw(MyException,std::runtime_error)</code> ) or
|
||||
using the <code>%catches</code> feature applied to the method.</p>
|
||||
|
||||
<p><em>Note: Using the %catches feature to define the
|
||||
handled exceptions is preferred to using exception specifications. If
|
||||
the interface is defined with an exception specification the generated
|
||||
swig proxy classes will have the same exception specification. In C++
|
||||
if exceptions other than those in the specification are thrown, the
|
||||
program will be terminated. </em></p>
|
||||
|
||||
<p>Because this default definition maps any unhandled java exceptions to
|
||||
Swig::DirectorException, any director methods that define exception
|
||||
specifications will cause program termination. To simply ignore
|
||||
unexpected exceptions, the default can be changed to:
|
||||
|
||||
<div class="code">
|
||||
<pre>
|
||||
%feature("director:except") %{
|
||||
jthrowable $error = jenv->ExceptionOccurred();
|
||||
if ($error) {
|
||||
jenv->ExceptionClear();
|
||||
$directorthrowshandlers
|
||||
return $null;
|
||||
}
|
||||
%}
|
||||
</pre>
|
||||
</div>
|
||||
</p>
|
||||
|
||||
<p>Alternatively an exception compatible with the existing director
|
||||
method exception specifications may be thrown. Assuming that all
|
||||
methods allow std::runtime_error to be thrown,
|
||||
the <code>return $null;</code> could be changed to:
|
||||
|
||||
<div class="code">
|
||||
<pre>
|
||||
throw std::runtime_error(Swig::JavaExceptionMessage(jenv,$error).message());
|
||||
</pre>
|
||||
</div>
|
||||
</p>
|
||||
|
||||
<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 <code>directorthrows</code> typemaps. The <code>directorthrows</code> typemap
|
||||
provides a code fragment to test for a pending java exception type, and the
|
||||
resulting C++ exception that will be thrown. In this example, a
|
||||
generic directorthrows typemap is appropriate for all three exceptions - all
|
||||
take single string constructors. If the constructors 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>
|
||||
// Define exceptions in header section using std::runtime_error
|
||||
%define DEFINE_EXCEPTION(NAME)
|
||||
%{
|
||||
#include <exception>
|
||||
namespace MyNS {
|
||||
struct NAME : public std::runtime_error { NAME(const std::string& what):runtime_error(what) {}; };
|
||||
}
|
||||
%}
|
||||
%enddef
|
||||
// Expose c++ exceptions as java Exceptions with getMessage
|
||||
%define DECLARE_EXCEPTION(NAME)
|
||||
%typemap(javabase) MyNS::NAME "java.lang.Exception";
|
||||
%rename(getMessage,fullname=1) MyNS::NAME::what;
|
||||
namespace MyNS {
|
||||
struct NAME {
|
||||
NAME(const std::string& what);
|
||||
const char * what();
|
||||
};
|
||||
}
|
||||
%enddef
|
||||
|
||||
DEFINE_EXCEPTION(ExceptionA)
|
||||
DEFINE_EXCEPTION(ExceptionB)
|
||||
DEFINE_EXCEPTION(Unknown)
|
||||
|
||||
// Mark three methods to map director-thrown exceptions.
|
||||
// Standard rules for feature matching apply
|
||||
%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 <code>directorthrows</code> 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. The code block
|
||||
in the directorthrows typemap should always throw a c++ exception.
|
||||
</p>
|
||||
|
||||
<p>Note that the <code>directorthrows</code> 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 Swig::DirectorException class provides enough information
|
||||
to reconstruct the original exception. In this case removing the
|
||||
$directorthrowshandlers replacement variable from the
|
||||
default <code>director:except</code> feature and simply always
|
||||
throwing a Swig::DirectorException 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, for each method which may get a generic
|
||||
DirectorException from a wrapped director method.</p>
|
||||
|
||||
<div class="code">
|
||||
<pre>
|
||||
%feature ("except",throws="Exception") MyClass::myMeth %{
|
||||
try { $action }
|
||||
catch (Swig::DirectorException & direxcp) {
|
||||
// jenv always available in JNI code
|
||||
// raise the java exception that originally caused the DirectorException
|
||||
direxcp.raiseJavaException(jenv);
|
||||
return $null;
|
||||
}
|
||||
%}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<p>The <code>throws="Exception"</code> attribute on the exception
|
||||
feature is necessary if any of the translated exceptions will be
|
||||
checked exceptions, since the java compiler will otherwise assert that
|
||||
no checked exceptions can be thrown by the method. This may be more
|
||||
specific that the completely generic "Exception" class, of course. A
|
||||
similar feature must be added to director methods to allow checked
|
||||
exceptions to be thrown from the director method implementations.
|
||||
Here, no actual exception handling is needed - the feature simply
|
||||
is being used to add a generic checked exception signature to the
|
||||
generated director method wrapper. </p>
|
||||
|
||||
<div class="code">
|
||||
<pre>
|
||||
%feature ("except",throws="Exception") MyDirectorClass::myDirMeth %{ %}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<H2><a name="Java_allprotected"></a>24.6 Accessing protected members</H2>
|
||||
|
||||
|
|
|
|||
|
|
@ -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. Feature director:except on <em>Class::method</em> with $directorthrowshandlers requires directorthrows typemap for exception <em>Exception</em>
|
||||
</ul>
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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") {
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
@ -44,8 +46,18 @@ JAVA_PACKAGE = $*
|
|||
JAVA_PACKAGEOPT = -package $(JAVA_PACKAGE)
|
||||
SWIGOPT += $(JAVA_PACKAGEOPT)
|
||||
|
||||
RUNME_CLASSPATH = -classpath .
|
||||
ifeq (Cygwin,$(shell uname -o 2>/dev/null))
|
||||
SEP=;
|
||||
else
|
||||
SEP=:
|
||||
endif
|
||||
|
||||
# Custom tests - tests with additional commandline options
|
||||
java_nspacewithoutpackage.%: JAVA_PACKAGEOPT =
|
||||
java_director_exception_feature_nspace.%: JAVA_PACKAGEOPT =
|
||||
java_director_exception_feature_nspace.%: RUNME_CLASSPATH = -classpath .$(SEP)./java_director_exception_feature_nspace
|
||||
|
||||
nspace.%: JAVA_PACKAGE = $*Package
|
||||
nspace_extend.%: JAVA_PACKAGE = $*Package
|
||||
director_nspace.%: JAVA_PACKAGE = $*Package
|
||||
|
|
@ -84,8 +96,8 @@ setup = \
|
|||
run_testcase = \
|
||||
cd $(JAVA_PACKAGE) && $(COMPILETOOL) $(JAVAC) -classpath . `find . -name "*.java"` && cd .. && \
|
||||
if [ -f $(srcdir)/$(SCRIPTPREFIX)$*$(SCRIPTSUFFIX) ]; then \
|
||||
$(COMPILETOOL) $(JAVAC) -classpath . -d . $(srcdir)/$(SCRIPTPREFIX)$*$(SCRIPTSUFFIX) && \
|
||||
env LD_LIBRARY_PATH="$(JAVA_PACKAGE):$$LD_LIBRARY_PATH" PATH="$(JAVA_PACKAGE):$$PATH" SHLIB_PATH="$(JAVA_PACKAGE):$$SHLIB_PATH" DYLD_LIBRARY_PATH="$(JAVA_PACKAGE):$$DYLD_LIBRARY_PATH" $(RUNTOOL) $(JAVA) $(JAVAFLAGS) -classpath . $*_runme; \
|
||||
$(COMPILETOOL) $(JAVAC) $(RUNME_CLASSPATH) -d . $(srcdir)/$(SCRIPTPREFIX)$*$(SCRIPTSUFFIX) && \
|
||||
env LD_LIBRARY_PATH="$(JAVA_PACKAGE):$$LD_LIBRARY_PATH" PATH="$(JAVA_PACKAGE):$$PATH" SHLIB_PATH="$(JAVA_PACKAGE):$$SHLIB_PATH" DYLD_LIBRARY_PATH="$(JAVA_PACKAGE):$$DYLD_LIBRARY_PATH" $(RUNTOOL) $(JAVA) $(JAVAFLAGS) $(RUNME_CLASSPATH) $*_runme; \
|
||||
fi
|
||||
|
||||
# Clean: remove testcase directories
|
||||
|
|
|
|||
|
|
@ -0,0 +1,150 @@
|
|||
import MyNS.*;
|
||||
import MyNS_JNI.*;
|
||||
|
||||
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 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_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, NewCheckedException, UnconstructableException {
|
||||
if (excp == 1)
|
||||
throw new MyJavaException1(java_director_exception_feature_nspace_Consts.GENERICPONGEXCP1);
|
||||
if (excp == 2)
|
||||
throw new NewCheckedException(java_director_exception_feature_nspace_Consts.GENERICPONGEXCP2);
|
||||
if (excp == 3)
|
||||
throw new NewUncheckedException(java_director_exception_feature_nspace_Consts.GENERICPONGEXCP3);
|
||||
if (excp == 4)
|
||||
throw new UnconstructableException(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 (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 (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");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
209
Examples/test-suite/java_director_exception_feature.i
Normal file
209
Examples/test-suite/java_director_exception_feature.i
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
%module(directors="1") java_director_exception_feature
|
||||
|
||||
%include <std_except.i>
|
||||
|
||||
%{
|
||||
#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,fullname=1) MyNS::Exception1;
|
||||
%rename(MyJavaException2,fullname=1) MyNS::Exception2;
|
||||
%rename(MyJavaUnexpected,fullname=1) MyNS::Unexpected;
|
||||
|
||||
%typemap(javabase) ::MyNS::Exception1,::MyNS::Exception2,::MyNS::Unexpected "java.lang.Exception";
|
||||
%rename(getMessage) what(); // Rename all what() methods
|
||||
|
||||
namespace MyNS {
|
||||
|
||||
struct Exception1 {
|
||||
Exception1(const std::string& what);
|
||||
const char * what();
|
||||
};
|
||||
struct Exception2 {
|
||||
Exception2(const std::string& what);
|
||||
const char * what();
|
||||
};
|
||||
struct Unexpected {
|
||||
Unexpected(const std::string& what);
|
||||
const char * what();
|
||||
};
|
||||
|
||||
}
|
||||
// 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;
|
||||
};
|
||||
|
||||
}
|
||||
%}
|
||||
216
Examples/test-suite/java_director_exception_feature_nspace.i
Normal file
216
Examples/test-suite/java_director_exception_feature_nspace.i
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
%module(directors="1") java_director_exception_feature_nspace
|
||||
|
||||
%include <std_except.i>
|
||||
|
||||
%nspace; // turn namespace feature on for everything.
|
||||
|
||||
// When using namespaces with no -package, must put JNI classes into a namespace
|
||||
%pragma(java) jniclasspackage=%{MyNS_JNI%}
|
||||
%warnfilter(826);
|
||||
|
||||
%{
|
||||
#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="MyNS.MyJavaException1") MyNS::Exception1 %{
|
||||
jclass excpcls = jenv->FindClass("MyNS/MyJavaException1");
|
||||
if (excpcls) {
|
||||
jenv->ThrowNew(excpcls, $1.what());
|
||||
}
|
||||
return $null;
|
||||
%}
|
||||
|
||||
%typemap(throws,throws="MyNS.MyJavaException1") int %{
|
||||
jclass excpcls = jenv->FindClass("MyNS/MyJavaException1");
|
||||
if (excpcls) {
|
||||
jenv->ThrowNew(excpcls, "Threw some integer");
|
||||
}
|
||||
return $null;
|
||||
%}
|
||||
|
||||
%typemap(throws,throws="MyNS.MyJavaException2") MyNS::Exception2 %{
|
||||
jclass excpcls = jenv->FindClass("MyNS/MyJavaException2");
|
||||
if (excpcls) {
|
||||
jenv->ThrowNew(excpcls, $1.what());
|
||||
}
|
||||
return $null;
|
||||
%}
|
||||
|
||||
|
||||
%typemap(throws,throws="MyNS.MyJavaUnexpected") MyNS::Unexpected %{
|
||||
jclass excpcls = jenv->FindClass("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,fullname=1) MyNS::Exception1;
|
||||
%rename(MyJavaException2,fullname=1) MyNS::Exception2;
|
||||
%rename(MyJavaUnexpected,fullname=1) MyNS::Unexpected;
|
||||
|
||||
%typemap(javabase) ::MyNS::Exception1,::MyNS::Exception2,::MyNS::Unexpected "java.lang.Exception";
|
||||
%rename(getMessage) what(); // Rename all what() methods
|
||||
|
||||
namespace MyNS {
|
||||
|
||||
struct Exception1 {
|
||||
Exception1(const std::string& what);
|
||||
const char * what();
|
||||
};
|
||||
struct Exception2 {
|
||||
Exception2(const std::string& what);
|
||||
const char * what();
|
||||
};
|
||||
struct Unexpected {
|
||||
Unexpected(const std::string& what);
|
||||
const char * what();
|
||||
};
|
||||
|
||||
}
|
||||
// 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;
|
||||
};
|
||||
|
||||
}
|
||||
%}
|
||||
|
|
@ -193,6 +193,242 @@ namespace Swig {
|
|||
};
|
||||
}
|
||||
|
||||
|
||||
// Default exception handler for all director methods.
|
||||
// Can override this for specific methods
|
||||
// This just documents the default. It is not actually attached as a feature
|
||||
// for efficiency reasons (and is defined in java.cxx) It can be overridden
|
||||
// as a feature for specific methods or globally.
|
||||
|
||||
//%feature("director:except") %{
|
||||
// jthrowable $error = jenv->ExceptionOccurred();
|
||||
// if ($error) {
|
||||
// jenv->ExceptionClear();
|
||||
// $directorthrowshandlers
|
||||
// throw Swig::DirectorException(jenv, $error);
|
||||
// }
|
||||
//%}
|
||||
|
||||
// Define some utility methods and classes. Cannot use fragments since
|
||||
// these may be used by %features and in directorthrows typemaps
|
||||
|
||||
#include <exception>
|
||||
|
||||
// Simple holder for exception messages, allowing access to a c-style string
|
||||
namespace Swig {
|
||||
struct JavaString {
|
||||
JavaString(JNIEnv * jenv, jstring jstr):jenv_(jenv), jstr_(jstr), cstr_(0) {
|
||||
if (jenv_ && jstr_) {
|
||||
// Get the null-terminated c-string out, and hold it
|
||||
cstr_ = (const char *) jenv_->GetStringUTFChars(jstr_, NULL);
|
||||
}
|
||||
}
|
||||
~JavaString() {
|
||||
if (jenv_ && jstr_ && cstr_) {
|
||||
jenv_->ReleaseStringUTFChars(jstr_, cstr_);
|
||||
}
|
||||
}
|
||||
const char *cstr() {
|
||||
return cstr_ ? cstr_ : "";
|
||||
}
|
||||
|
||||
private:
|
||||
JNIEnv * jenv_;
|
||||
jstring jstr_;
|
||||
const char * cstr_;
|
||||
|
||||
// non-copyable
|
||||
JavaString(const JavaString &);
|
||||
JavaString & operator=(const JavaString &);
|
||||
};
|
||||
|
||||
struct JavaExceptionMessage {
|
||||
JavaExceptionMessage(JNIEnv * jenv, jthrowable excp) :
|
||||
jstrholder_(jenv,exceptionMsgJstr(jenv,excp)) {
|
||||
}
|
||||
~JavaExceptionMessage() {
|
||||
}
|
||||
const char *message() {
|
||||
return jstrholder_.cstr();
|
||||
}
|
||||
|
||||
private:
|
||||
JavaString jstrholder_;
|
||||
|
||||
// non-copyable
|
||||
JavaExceptionMessage(const JavaExceptionMessage &);
|
||||
JavaExceptionMessage & operator=(const JavaExceptionMessage &);
|
||||
|
||||
// Static method to initialize jstrholder_
|
||||
static jstring exceptionMsgJstr(JNIEnv * jenv, jthrowable excp) {
|
||||
jstring jmsg = NULL;
|
||||
if (jenv && excp) {
|
||||
jenv->ExceptionClear(); // Cannot invoke methods with pending exception
|
||||
jclass thrwclz = jenv->GetObjectClass(excp);
|
||||
if (thrwclz) {
|
||||
// if no getMessage() or other exception, no msg available.
|
||||
jmethodID getThrowableMsgMethodID =
|
||||
jenv->GetMethodID(thrwclz, "getMessage", "()Ljava/lang/String;");
|
||||
if (getThrowableMsgMethodID && !jenv->ExceptionCheck()) {
|
||||
// if problem accessing exception message string, no msg available.
|
||||
jmsg = (jstring)
|
||||
jenv->CallObjectMethod(excp, getThrowableMsgMethodID);
|
||||
}
|
||||
}
|
||||
if (jmsg == NULL && jenv->ExceptionCheck()) {
|
||||
jenv->ExceptionClear();
|
||||
}
|
||||
}
|
||||
return jmsg;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////
|
||||
|
||||
bool ExceptionMatches(JNIEnv * jenv, jthrowable excp,
|
||||
const char *clzname) {
|
||||
jboolean matches = false;
|
||||
|
||||
if (excp && jenv && clzname) {
|
||||
// Have to clear exceptions for correct behavior. Code around
|
||||
// ExceptionMatches should restore pending exception if
|
||||
// desired - already have throwable.
|
||||
jenv->ExceptionClear();
|
||||
|
||||
jclass clz = jenv->FindClass(clzname);
|
||||
if (clz && ! jenv->ExceptionCheck()) {
|
||||
jclass classclz = jenv->GetObjectClass(clz);
|
||||
jmethodID isInstanceMethodID =
|
||||
jenv->GetMethodID(classclz, "isInstance", "(Ljava/lang/Object;)Z");
|
||||
if (isInstanceMethodID) {
|
||||
matches = (jboolean)
|
||||
jenv->CallBooleanMethod(clz, isInstanceMethodID, excp);
|
||||
}
|
||||
}
|
||||
// This may happen if user typemaps or director:except
|
||||
// features call ExceptionMatches incorrectly, typically with
|
||||
// an invalid clzname argument. Uncommenting the debug lines
|
||||
// may help to diagnose.
|
||||
// As is, this leaves the underlying case as a pending exception
|
||||
// which may not be that clear (e.g. ClassNotFoundException)
|
||||
|
||||
// if (jenv->ExceptionCheck()) {
|
||||
// JavaExceptionMessage jstrmsg(jenv,jenv->ExceptionOccurred());
|
||||
// std::cerr << "Error: ExceptionMatches: class '" <<
|
||||
// clzname << "' : " << jstrmsg.message() << std::endl;
|
||||
// }
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
// Provide the class name to allow reconstruction of the original exception
|
||||
struct DirectorException : std::exception {
|
||||
|
||||
// Construct a DirectorException from a java throwable
|
||||
DirectorException(JNIEnv* jenv,jthrowable excp) : classname_(0), msg_(0) {
|
||||
jstring jstr_classname = NULL;
|
||||
jmethodID mid_getName = NULL;
|
||||
jclass thrwclz;
|
||||
jclass clzclz;
|
||||
|
||||
if (excp) {
|
||||
// Get the exception class, like Exception
|
||||
thrwclz = jenv->GetObjectClass(excp);
|
||||
if (thrwclz) {
|
||||
// Get the java.lang.Class class
|
||||
clzclz = jenv->GetObjectClass(thrwclz);
|
||||
if (clzclz) {
|
||||
mid_getName = jenv->GetMethodID(clzclz, "getName", "()Ljava/lang/String;");
|
||||
if (mid_getName) {
|
||||
// Get the excp class name
|
||||
jstr_classname = (jstring)(jenv->CallObjectMethod(thrwclz, mid_getName));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Copy strings, since no guarantee jenv will be active when handled
|
||||
// If classname_ is 0, returned as "UnknownException"
|
||||
if (jstr_classname) {
|
||||
JavaString classname(jenv, jstr_classname);
|
||||
classname_ = copypath(classname.cstr());
|
||||
}
|
||||
JavaExceptionMessage exceptionmsg(jenv, excp);
|
||||
msg_ = copystr(exceptionmsg.message());
|
||||
}
|
||||
|
||||
// Throw as a wrapped Runtime Error explicitly.
|
||||
DirectorException(const char * msg) : classname_(0), msg_(0) {
|
||||
classname_ = copypath("java/lang/RuntimeError");
|
||||
msg_ = copystr(msg);
|
||||
}
|
||||
|
||||
~DirectorException() throw() {
|
||||
delete[] classname_;
|
||||
delete[] msg_;
|
||||
}
|
||||
|
||||
// If there was problem finding classname, keep track of error
|
||||
// On raiseJavaException will be mapped to a RuntimeException
|
||||
const char* classname() const throw() {
|
||||
return classname_ ? classname_ : "UnknownException";
|
||||
}
|
||||
const char* what() const throw() {
|
||||
return msg_ ? msg_ : "";
|
||||
}
|
||||
|
||||
// Python director code provides static raise() methods
|
||||
// Omitted here: Seems less good than
|
||||
// throw DirectorException(jenv, excp)
|
||||
// from which compiler can infer a C++ exception is thrown.
|
||||
|
||||
// Reconstruct and raise the Java Exception that caused the DirectorException
|
||||
void raiseJavaException(JNIEnv* jenv) {
|
||||
if (jenv) {
|
||||
jenv->ExceptionClear();
|
||||
|
||||
jmethodID strCtorID = 0;
|
||||
|
||||
jclass excpclz = jenv->FindClass(classname());
|
||||
|
||||
if (excpclz) {
|
||||
strCtorID = jenv->GetMethodID(excpclz,"<init>","(Ljava/lang/String;)V");
|
||||
}
|
||||
|
||||
if (strCtorID) {
|
||||
// If exception has a string constructor, throw an instance
|
||||
jenv->ThrowNew(excpclz, what());
|
||||
} else {
|
||||
// Else, throw a runtime
|
||||
SWIG_JavaThrowException(jenv, SWIG_JavaRuntimeException, what() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const char * classname_;
|
||||
const char * msg_;
|
||||
|
||||
static const char * copypath(const char * srcmsg) {
|
||||
return copystr(srcmsg, 1);
|
||||
}
|
||||
static const char * copystr(const char * srcmsg, int pathrepl=0) {
|
||||
char * target = 0;
|
||||
if (srcmsg) {
|
||||
int msglen = 1+strlen(srcmsg); //+1 for null terminator
|
||||
target = new char[msglen];
|
||||
strncpy(target, srcmsg, msglen);
|
||||
}
|
||||
// If pathrepl, replace any '.' with '/'
|
||||
if (pathrepl) {
|
||||
for(char *c=target; *c; ++c) {
|
||||
if ('.' == *c) *c = '/';
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,8 @@ 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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -742,15 +742,25 @@ public:
|
|||
*----------------------------------------------------------------------*/
|
||||
|
||||
UpcallData *addUpcallMethod(String *imclass_method, String *class_method, String *imclass_desc, String *class_desc, String *decl) {
|
||||
UpcallData *udata;
|
||||
String *imclass_methodidx;
|
||||
String *class_methodidx;
|
||||
Hash *new_udata;
|
||||
String *key = NewStringf("%s|%s", imclass_method, decl);
|
||||
|
||||
++curr_class_dmethod;
|
||||
|
||||
String *imclass_methodidx = NewStringf("%d", n_dmethods);
|
||||
String *class_methodidx = NewStringf("%d", n_dmethods - first_class_dmethod);
|
||||
/* Do we know about this director class already? */
|
||||
if ((udata = Getattr(dmethods_table, key))) {
|
||||
Delete(key);
|
||||
return Getattr(udata, "methodoff");
|
||||
}
|
||||
|
||||
imclass_methodidx = NewStringf("%d", n_dmethods);
|
||||
class_methodidx = NewStringf("%d", n_dmethods - first_class_dmethod);
|
||||
n_dmethods++;
|
||||
|
||||
Hash *new_udata = NewHash();
|
||||
new_udata = NewHash();
|
||||
Append(dmethods_seq, new_udata);
|
||||
Setattr(dmethods_table, key, new_udata);
|
||||
|
||||
|
|
@ -2939,20 +2949,36 @@ public:
|
|||
* 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 '.'
|
||||
* p - a parameter type, that may hold a javapackage typemap. If passed,
|
||||
* the $packagepath will be substituted, but call method below instead.
|
||||
* Outputs:
|
||||
* tm - typemap contents complete with the special variable substitution
|
||||
* Return:
|
||||
* substitution_performed - flag indicating if a substitution was performed
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
bool substituteClassname(SwigType *pt, String *tm, bool jnidescriptor = false) {
|
||||
bool substituteClassname(SwigType *pt, String *tm,
|
||||
bool jnidescriptor = false, Parm * pkgpathparm = 0)
|
||||
{
|
||||
return substituteClassnameAndPackagePath( pt, tm, jnidescriptor, pkgpathparm);
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* substituteClassnameAndPackagePath()
|
||||
* Used to canonicalize JNI descriptors, including code emitting director throws typemaps.
|
||||
*
|
||||
* Only usage always has jnidescriptor AND p set. Maybe collapse args.
|
||||
* ----------------------------------------------------------------------------- */
|
||||
bool substituteClassnameAndPackagePath(SwigType *pt, String *tm,
|
||||
bool jnidescriptor, Parm * pkgpathparm)
|
||||
{
|
||||
bool substitution_performed = false;
|
||||
SwigType *type = Copy(SwigType_typedef_resolve_all(pt));
|
||||
SwigType *strippedtype = SwigType_strip_qualifiers(type);
|
||||
|
||||
if (Strstr(tm, "$javaclassname")) {
|
||||
SwigType *classnametype = Copy(strippedtype);
|
||||
substituteClassnameSpecialVariable(classnametype, tm, "$javaclassname", jnidescriptor);
|
||||
substituteClassnameSpecialVariable(classnametype, tm, "$javaclassname", jnidescriptor, pkgpathparm);
|
||||
substitution_performed = true;
|
||||
Delete(classnametype);
|
||||
}
|
||||
|
|
@ -2960,7 +2986,7 @@ public:
|
|||
SwigType *classnametype = Copy(strippedtype);
|
||||
Delete(SwigType_pop(classnametype));
|
||||
if (Len(classnametype) > 0) {
|
||||
substituteClassnameSpecialVariable(classnametype, tm, "$*javaclassname", jnidescriptor);
|
||||
substituteClassnameSpecialVariable(classnametype, tm, "$*javaclassname", jnidescriptor, pkgpathparm);
|
||||
substitution_performed = true;
|
||||
}
|
||||
Delete(classnametype);
|
||||
|
|
@ -2968,7 +2994,7 @@ public:
|
|||
if (Strstr(tm, "$&javaclassname")) {
|
||||
SwigType *classnametype = Copy(strippedtype);
|
||||
SwigType_add_pointer(classnametype);
|
||||
substituteClassnameSpecialVariable(classnametype, tm, "$&javaclassname", jnidescriptor);
|
||||
substituteClassnameSpecialVariable(classnametype, tm, "$&javaclassname", jnidescriptor, pkgpathparm);
|
||||
substitution_performed = true;
|
||||
Delete(classnametype);
|
||||
}
|
||||
|
|
@ -2982,27 +3008,46 @@ public:
|
|||
/* -----------------------------------------------------------------------------
|
||||
* substituteClassnameSpecialVariable()
|
||||
* ----------------------------------------------------------------------------- */
|
||||
void substituteClassnameSpecialVariable(SwigType *classnametype, String *tm, const char *classnamespecialvariable, bool jnidescriptor, Parm * pkgpathparm) {
|
||||
String * replacementname;
|
||||
|
||||
void substituteClassnameSpecialVariable(SwigType *classnametype, String *tm, const char *classnamespecialvariable, bool jnidescriptor) {
|
||||
if (SwigType_isenum(classnametype)) {
|
||||
String *enumname = getEnumName(classnametype, jnidescriptor);
|
||||
if (enumname)
|
||||
Replaceall(tm, classnamespecialvariable, enumname);
|
||||
else
|
||||
Replaceall(tm, classnamespecialvariable, NewStringf("int"));
|
||||
replacementname = Copy(getEnumName(classnametype, jnidescriptor));
|
||||
if (!replacementname) {
|
||||
replacementname = NewString("int");
|
||||
}
|
||||
} else {
|
||||
String *classname = getProxyName(classnametype);
|
||||
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(getProxyName(classnametype));
|
||||
if (! replacementname) {
|
||||
// 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 (pkgpathparm) {
|
||||
if (Strchr(replacementname, '.') != NULL) {
|
||||
// nspace feature in use, indicated by dots (!?)
|
||||
// if replacementname is in name space form, a.b.c, discard
|
||||
// packagepath from any string like $packagepath/$javaclassname
|
||||
// since IT WAS ADDED in getProxyName
|
||||
// But $packagepath could still be used by itself in same string
|
||||
// since this is used to emit general code blocks for director
|
||||
// exceptions
|
||||
// So do this to get rid of all combined forms before subsitituting
|
||||
Replaceall(tm, "$packagepath/$javaclassname","$javaclassname");
|
||||
Replaceall(tm, "$packagepath/$*javaclassname","$*javaclassname");
|
||||
Replaceall(tm, "$packagepath/$&javaclassname","$&javaclassname");
|
||||
}
|
||||
substitutePackagePath(tm, pkgpathparm);
|
||||
}
|
||||
if (jnidescriptor) {
|
||||
Replaceall(replacementname,".","/");
|
||||
}
|
||||
Replaceall(tm, classnamespecialvariable, replacementname);
|
||||
|
||||
Delete(replacementname);
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
|
|
@ -3497,11 +3542,32 @@ public:
|
|||
// Delete(method_attr);
|
||||
}
|
||||
|
||||
/** Replace $packagepath using the javapackage typemap
|
||||
associated with passed parm or global package_path
|
||||
if none. "$packagepath/" is replaced with "" if no
|
||||
package path is set.
|
||||
*/
|
||||
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 = package_path;
|
||||
|
||||
if (Len(pkg_path) > 0) {
|
||||
Replaceall(text, "$packagepath", pkg_path);
|
||||
} else {
|
||||
Replaceall(text, "$packagepath/", empty_string);
|
||||
Replaceall(text, "$packagepath", empty_string);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* Canonicalize the JNI field descriptor
|
||||
*
|
||||
* Replace the $javapackage and $javaclassname family of special
|
||||
* variables with the desired package and Java proxy name as
|
||||
* Replace the $packagepath and $javaclassname family of
|
||||
* variables with the desired package and Java descriptor as
|
||||
* required in the JNI field descriptors.
|
||||
*
|
||||
* !!SFM!! If $packagepath occurs in the field descriptor, but
|
||||
|
|
@ -3511,27 +3577,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);
|
||||
// This returns a JNI descriptor, in path format
|
||||
substituteClassnameAndPackagePath(type, descriptor_out, true, p);
|
||||
|
||||
return descriptor_out;
|
||||
}
|
||||
|
|
@ -3928,17 +3978,33 @@ public:
|
|||
// Get any Java exception classes in the throws typemap
|
||||
ParmList *throw_parm_list = NULL;
|
||||
|
||||
// May need to add throws to director classes 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);
|
||||
// If %catches feature, it overrides specified throws().
|
||||
if (! catches_list) {
|
||||
addThrows(n, "tmap:throws", p);
|
||||
}
|
||||
|
||||
if (gencomma++) {
|
||||
Append(w->def, ", ");
|
||||
|
|
@ -4001,7 +4067,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, c_classname, name);
|
||||
|
||||
if (!is_void) {
|
||||
String *jresult_str = NewString("jresult");
|
||||
|
|
@ -4042,7 +4109,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");
|
||||
|
|
@ -4098,6 +4166,79 @@ public:
|
|||
return status;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------
|
||||
* directorExcept handler: Emit code to map java exceptions
|
||||
* back to C++ exceptions when feature("director:except") is appied
|
||||
* to a method node
|
||||
* ------------------------------------------------------------ */
|
||||
void directorExceptHandler(Node *n, ParmList *throw_parm_list, Wrapper *w, String *c_classname, String *name) {
|
||||
// After director java method call, allow code for director method exception to be added
|
||||
// Look for director:exceptfeature
|
||||
Parm *p;
|
||||
|
||||
// "Default" feature, so that feature is not applied to every node, per W. Fulton
|
||||
static char const * DEFAULT_DIREXCP_FEATURE =
|
||||
"jthrowable $error = jenv->ExceptionOccurred();\n"
|
||||
"if ($error) {\n"
|
||||
" jenv->ExceptionClear();\n"
|
||||
" $directorthrowshandlers\n"
|
||||
" throw Swig::DirectorException(jenv, $error);\n"
|
||||
"}\n";
|
||||
|
||||
String * featdirexcp = Getattr(n, "feature:director:except");
|
||||
|
||||
if (!featdirexcp) {
|
||||
featdirexcp = NewString(DEFAULT_DIREXCP_FEATURE);
|
||||
} else {
|
||||
featdirexcp = Copy(featdirexcp);
|
||||
}
|
||||
// Can explicitly disable director:except by setting to "" or "0"
|
||||
if (0 != Len(featdirexcp) && 0 != Cmp(featdirexcp,"0")) {
|
||||
|
||||
// Replace any $packagepath with global -package package_path
|
||||
substitutePackagePath(featdirexcp, 0);
|
||||
|
||||
// Replace $action with any defined typemap handlers (or nothing)
|
||||
if (Strstr(featdirexcp, "$directorthrowshandlers")) {
|
||||
String *directorthrowshandlers_code = NewString("");
|
||||
|
||||
for (p = throw_parm_list; p; p = nextSibling(p)) {
|
||||
String *tmapdirthrows = Getattr(p, "tmap:directorthrows");
|
||||
String * excptype = Getattr(p,"type");
|
||||
|
||||
if (!tmapdirthrows) {
|
||||
Swig_warning(WARN_TYPEMAP_DIRECTORTHROWS_UNDEF, input_file, line_number,
|
||||
"Feature director:except on %s::%s with $directorthrowshandlers requires directorthrows typemap for exception %s.\n",
|
||||
SwigType_namestr(c_classname),SwigType_namestr(name), excptype);
|
||||
} else {
|
||||
// replace $packagepath
|
||||
tmapdirthrows = Copy(tmapdirthrows);
|
||||
substituteClassnameAndPackagePath(excptype, tmapdirthrows, true, p);
|
||||
|
||||
Printf(directorthrowshandlers_code,
|
||||
"// Handle exception %s using directorthrows typemap\n"
|
||||
"%s",
|
||||
excptype, tmapdirthrows);
|
||||
Delete(tmapdirthrows);
|
||||
}
|
||||
}
|
||||
// Delete extra new line if no handlers.
|
||||
while (Replaceall(featdirexcp, "$directorthrowshandlers ",
|
||||
"$directorthrowshandlers")) {}
|
||||
if (0 == Len(directorthrowshandlers_code))
|
||||
Replaceall(featdirexcp, "$directorthrowshandlers\n", "");
|
||||
else
|
||||
Replaceall(featdirexcp, "$directorthrowshandlers", directorthrowshandlers_code);
|
||||
Delete(directorthrowshandlers_code);
|
||||
}
|
||||
|
||||
// Replace all occurrences of $error with common var name.
|
||||
Replaceall(featdirexcp, "$error", "thrown_");
|
||||
Printf(w->code, " %s\n", featdirexcp);
|
||||
}
|
||||
Delete(featdirexcp);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------
|
||||
* directorPrefixArgs()
|
||||
* ------------------------------------------------------------ */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue