Rework the director.swg changes for director exception handling
- More robust implementation. - Fix some bugs to give better exception messages when a user uses the director utility exception functions and classes. - Replace unnecessarily shortened variable names for easier reading of code.
This commit is contained in:
parent
fdc1772e38
commit
e7a6be289e
1 changed files with 95 additions and 116 deletions
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
||||||
#if defined(DEBUG_DIRECTOR_OWNED)
|
#if defined(DEBUG_DIRECTOR_OWNED) || defined(DEBUG_DIRECTOR_EXCEPTION)
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -196,43 +196,9 @@ namespace Swig {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Utility methods and classes for exception handling.
|
// Utility classes and functions for exception handling.
|
||||||
|
|
||||||
// Helper method to determine if a Java throwable matches a particular Java class type
|
// Simple holder for a Java string during exception handling, providing access to a c-style string
|
||||||
bool ExceptionMatches(JNIEnv *jenv, jthrowable excp, const char *clzname) {
|
|
||||||
jboolean matches = false;
|
|
||||||
|
|
||||||
if (excp && jenv && clzname) {
|
|
||||||
// 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(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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simple holder for a Java string during exception handling, allowing access to a c-style string
|
|
||||||
class JavaString {
|
class JavaString {
|
||||||
public:
|
public:
|
||||||
JavaString(JNIEnv *jenv, jstring jstr) : jenv_(jenv), jstr_(jstr), cstr_(0) {
|
JavaString(JNIEnv *jenv, jstring jstr) : jenv_(jenv), jstr_(jstr), cstr_(0) {
|
||||||
|
|
@ -245,8 +211,8 @@ namespace Swig {
|
||||||
jenv_->ReleaseStringUTFChars(jstr_, cstr_);
|
jenv_->ReleaseStringUTFChars(jstr_, cstr_);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *cstr() {
|
const char *c_str(const char *null_string = "null JavaString") const {
|
||||||
return cstr_ ? cstr_ : "";
|
return cstr_ ? cstr_ : null_string;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -259,17 +225,14 @@ namespace Swig {
|
||||||
const char *cstr_;
|
const char *cstr_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper to extract the exception message from a Java throwable
|
// Helper class to extract the exception message from a Java throwable
|
||||||
class JavaExceptionMessage {
|
class JavaExceptionMessage {
|
||||||
public:
|
public:
|
||||||
JavaExceptionMessage(JNIEnv *jenv, jthrowable excp) : jstrholder_(jenv, exceptionMsgJstr(jenv, excp)) {
|
JavaExceptionMessage(JNIEnv *jenv, jthrowable throwable) : message_(jenv, exceptionMessageFromThrowable(jenv, throwable)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
~JavaExceptionMessage() {
|
const char *message() const {
|
||||||
}
|
return message_.c_str("Could not get exception message in JavaExceptionMessage");
|
||||||
|
|
||||||
const char *message() {
|
|
||||||
return jstrholder_.cstr();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -277,67 +240,61 @@ namespace Swig {
|
||||||
JavaExceptionMessage(const JavaExceptionMessage &);
|
JavaExceptionMessage(const JavaExceptionMessage &);
|
||||||
JavaExceptionMessage &operator=(const JavaExceptionMessage &);
|
JavaExceptionMessage &operator=(const JavaExceptionMessage &);
|
||||||
|
|
||||||
// Static method to initialize jstrholder_
|
// Get exception message by calling Java method Throwable.getMessage()
|
||||||
static jstring exceptionMsgJstr(JNIEnv *jenv, jthrowable excp) {
|
static jstring exceptionMessageFromThrowable(JNIEnv *jenv, jthrowable throwable) {
|
||||||
jstring jmsg = NULL;
|
jstring jmsg = NULL;
|
||||||
if (jenv && excp) {
|
if (jenv && throwable) {
|
||||||
jenv->ExceptionClear(); // Cannot invoke methods with pending exception
|
jenv->ExceptionClear(); // Cannot invoke methods with any pending exceptions
|
||||||
jclass thrwclz = jenv->GetObjectClass(excp);
|
jclass throwclz = jenv->GetObjectClass(throwable);
|
||||||
if (thrwclz) {
|
if (throwclz) {
|
||||||
// if no getMessage() or other exception, no msg available.
|
// All Throwable classes have a getMessage() method, so call it to extract the exception message
|
||||||
jmethodID getThrowableMsgMethodID = jenv->GetMethodID(thrwclz, "getMessage", "()Ljava/lang/String;");
|
jmethodID getMessageMethodID = jenv->GetMethodID(throwclz, "getMessage", "()Ljava/lang/String;");
|
||||||
if (getThrowableMsgMethodID && !jenv->ExceptionCheck()) {
|
if (getMessageMethodID)
|
||||||
// if problem accessing exception message string, no msg will be available.
|
jmsg = (jstring)jenv->CallObjectMethod(throwable, getMessageMethodID);
|
||||||
jmsg = (jstring)jenv->CallObjectMethod(excp, getThrowableMsgMethodID);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (jmsg == NULL && jenv->ExceptionCheck()) {
|
if (jmsg == NULL && jenv->ExceptionCheck())
|
||||||
jenv->ExceptionClear();
|
jenv->ExceptionClear();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return jmsg;
|
return jmsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
JavaString jstrholder_;
|
JavaString message_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// C++ Exception class for converting from Java exceptions thrown during a director method Java upcall
|
// C++ Exception class for handling Java exceptions thrown during a director method Java upcall
|
||||||
class DirectorException : public std::exception {
|
class DirectorException : public std::exception {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// Construct a DirectorException from a Java throwable
|
// Construct exception from a Java throwable
|
||||||
DirectorException(JNIEnv *jenv, jthrowable excp) : classname_(0), msg_(0) {
|
DirectorException(JNIEnv *jenv, jthrowable throwable) : classname_(0), msg_(0) {
|
||||||
jstring jstr_classname = NULL;
|
|
||||||
|
|
||||||
if (excp) {
|
// Call Java method Object.getClass().getName() to obtain the throwable's class name (delimited by '/')
|
||||||
// Get the exception class, like Exception
|
if (throwable) {
|
||||||
jclass thrwclz = jenv->GetObjectClass(excp);
|
jclass throwclz = jenv->GetObjectClass(throwable);
|
||||||
if (thrwclz) {
|
if (throwclz) {
|
||||||
// Get the Java.lang.Class class
|
jclass clzclz = jenv->GetObjectClass(throwclz);
|
||||||
jclass clzclz = jenv->GetObjectClass(thrwclz);
|
|
||||||
if (clzclz) {
|
if (clzclz) {
|
||||||
jmethodID mid_getName = mid_getName = jenv->GetMethodID(clzclz, "getName", "()Ljava/lang/String;");
|
jmethodID getNameMethodID = jenv->GetMethodID(clzclz, "getName", "()Ljava/lang/String;");
|
||||||
if (mid_getName) {
|
if (getNameMethodID) {
|
||||||
// Get the excp class name
|
jstring jstr_classname = (jstring)(jenv->CallObjectMethod(throwclz, getNameMethodID));
|
||||||
jstr_classname = (jstring)(jenv->CallObjectMethod(thrwclz, mid_getName));
|
// 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Copy strings, since no guarantee jenv will be active when handled
|
|
||||||
// If classname_ is 0, returned as "UnknownException"
|
JavaExceptionMessage exceptionmsg(jenv, throwable);
|
||||||
if (jstr_classname) {
|
|
||||||
JavaString classname(jenv, jstr_classname);
|
|
||||||
classname_ = copypath(classname.cstr());
|
|
||||||
}
|
|
||||||
JavaExceptionMessage exceptionmsg(jenv, excp);
|
|
||||||
msg_ = copystr(exceptionmsg.message());
|
msg_ = copystr(exceptionmsg.message());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Throw as a wrapped RuntimeError explicitly.
|
// More general constructor for handling as a java.lang.RuntimeException
|
||||||
DirectorException(const char *msg) : classname_(0), msg_(0) {
|
DirectorException(const char *msg) : classname_(0), msg_(copystr(msg ? msg : "Unspecified DirectorException message")) {
|
||||||
classname_ = copypath("java/lang/RuntimeError");
|
|
||||||
msg_ = copystr(msg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~DirectorException() throw() {
|
~DirectorException() throw() {
|
||||||
|
|
@ -345,57 +302,49 @@ namespace Swig {
|
||||||
delete[] msg_;
|
delete[] msg_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there was a 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() {
|
const char *what() const throw() {
|
||||||
return msg_ ? msg_ : "";
|
return msg_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reconstruct and raise the Java Exception that caused the DirectorException
|
// Reconstruct and raise/throw the Java Exception that caused the DirectorException
|
||||||
void raiseJavaException(JNIEnv *jenv) {
|
// Note that any error in the JNI exception handling results in a Java RuntimeException
|
||||||
|
void raiseJavaException(JNIEnv *jenv) const {
|
||||||
if (jenv) {
|
if (jenv) {
|
||||||
jenv->ExceptionClear();
|
jenv->ExceptionClear();
|
||||||
|
|
||||||
jmethodID strCtorID = 0;
|
jmethodID ctorMethodID = 0;
|
||||||
jclass excpclz = jenv->FindClass(classname());
|
jclass throwableclass = 0;
|
||||||
|
if (classname_) {
|
||||||
if (excpclz) {
|
throwableclass = jenv->FindClass(classname_);
|
||||||
strCtorID = jenv->GetMethodID(excpclz,"<init>","(Ljava/lang/String;)V");
|
if (throwableclass)
|
||||||
|
ctorMethodID = jenv->GetMethodID(throwableclass, "<init>", "(Ljava/lang/String;)V");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strCtorID) {
|
if (ctorMethodID) {
|
||||||
// If exception has a string constructor, throw an instance
|
jenv->ThrowNew(throwableclass, what());
|
||||||
jenv->ThrowNew(excpclz, what());
|
|
||||||
} else {
|
} else {
|
||||||
// Else, throw a runtime
|
SWIG_JavaThrowException(jenv, SWIG_JavaRuntimeException, what());
|
||||||
SWIG_JavaThrowException(jenv, SWIG_JavaRuntimeException, what() );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const char *copypath(const char *srcmsg) {
|
static char *copypath(const char *srcmsg) {
|
||||||
return copystr(srcmsg, 1);
|
char *target = copystr(srcmsg);
|
||||||
|
for (char *c=target; *c; ++c) {
|
||||||
|
if ('.' == *c)
|
||||||
|
*c = '/';
|
||||||
|
}
|
||||||
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *copystr(const char *srcmsg, int pathrepl=0) {
|
static char *copystr(const char *srcmsg) {
|
||||||
char *target = 0;
|
char *target = 0;
|
||||||
if (srcmsg) {
|
if (srcmsg) {
|
||||||
int msglen = 1 + strlen(srcmsg);
|
int msglen = strlen(srcmsg) + 1;
|
||||||
target = new char[msglen];
|
target = new char[msglen];
|
||||||
strncpy(target, srcmsg, msglen);
|
strncpy(target, srcmsg, msglen);
|
||||||
}
|
}
|
||||||
// If pathrepl, replace any '.' with '/'
|
|
||||||
if (pathrepl) {
|
|
||||||
for (char *c=target; *c; ++c) {
|
|
||||||
if ('.' == *c)
|
|
||||||
*c = '/';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -403,6 +352,36 @@ namespace Swig {
|
||||||
const char *msg_;
|
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 */
|
#endif /* __cplusplus */
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue