Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What happens when I throw a C++ exception from a native Java method?

Suppose I'm embedding Sun's JVM in a C++ application. Through JNI I call a Java method (my own), which in turns calls a native method I implemented in a shared library.

What happens if this native method throws a C++ exception?

edit: compiler is gcc 3.4.x, jvm is sun's 1.6.20.

like image 407
Idan K Avatar asked Nov 09 '10 20:11

Idan K


People also ask

What happens when a program throws an exception in Java?

When an exception is thrown using the throw keyword, the flow of execution of the program is stopped and the control is transferred to the nearest enclosing try-catch block that matches the type of exception thrown. If no such match is found, the default exception handler terminates the program.

What happens when a method throws an exception?

It eases the handle exception to the user. Because then all of the exceptions can be handled in a method which is used to run. Mostly it is mainly a method, so that the user does not need to explore inside the method. It also throws keyword force to the compiler to handle the exception which could be occurring.

Does a method stop after throwing an exception?

All methods in the call stack between the method throwing the exception and the method catching it have their execution stopped at the point in the code where the exception is thrown or propagated.

Can you throw exceptions in C?

C doesn't support exceptions. You can try compiling your C code as C++ with Visual Studio or G++ and see if it'll compile as-is. Most C applications will compile as C++ without major changes, and you can then use the try... catch syntax.


2 Answers

The Java compiler doesn't understand C++ exceptions, so you'll have to work with both Java and C++ exceptions. Luckily, that's not overly complicated. First we have a C++ exception that tells us if a Java exception has occurred.

#include <stdexcept> //This is how we represent a Java exception already in progress struct ThrownJavaException : std::runtime_error {     ThrownJavaException() :std::runtime_error("") {}     ThrownJavaException(const std::string& msg ) :std::runtime_error(msg) {} }; 

and a function to throw an C++ exception if a Java exception is already in place:

inline void assert_no_exception(JNIEnv * env) {     if (env->ExceptionCheck()==JNI_TRUE)          throw ThrownJavaException("assert_no_exception"); } 

we also have a C++ exception for throwing new Java exceptions:

//used to throw a new Java exception. use full paths like: //"java/lang/NoSuchFieldException" //"java/lang/NullPointerException" //"java/security/InvalidParameterException" struct NewJavaException : public ThrownJavaException{     NewJavaException(JNIEnv * env, const char* type="", const char* message="")         :ThrownJavaException(type+std::string(" ")+message)     {         jclass newExcCls = env->FindClass(type);         if (newExcCls != NULL)             env->ThrowNew(newExcCls, message);         //if it is null, a NoClassDefFoundError was already thrown     } }; 

We also need a function to swallow C++ exceptions and replace them with Java exceptions

void swallow_cpp_exception_and_throw_java(JNIEnv * env) {     try {         throw;     } catch(const ThrownJavaException&) {         //already reported to Java, ignore     } catch(const std::bad_alloc& rhs) {         //translate OOM C++ exception to a Java exception         NewJavaException(env, "java/lang/OutOfMemoryError", rhs.what());      } catch(const std::ios_base::failure& rhs) { //sample translation         //translate IO C++ exception to a Java exception         NewJavaException(env, "java/io/IOException", rhs.what());       //TRANSLATE ANY OTHER C++ EXCEPTIONS TO JAVA EXCEPTIONS HERE      } catch(const std::exception& e) {         //translate unknown C++ exception to a Java exception         NewJavaException(env, "java/lang/Error", e.what());     } catch(...) {         //translate unknown C++ exception to a Java exception         NewJavaException(env, "java/lang/Error", "Unknown exception type");     } } 

With the above functions, it's easy to use Java/C++ hybrid exceptions in your C++ code, as shown below.

extern "C" JNIEXPORT  void JNICALL Java_MyClass_MyFunc(JNIEnv * env, jclass jc_, jstring param) {     try { //do not let C++ exceptions outside of this function          //YOUR CODE BELOW THIS LINE.  HERES SOME RANDOM CODE         if (param == NULL) //if something is wrong, throw a java exception              throw NewJavaException(env, "java/lang/NullPointerException", "param");                     do_stuff(param); //might throw java or C++ exceptions         assert_no_exception(env); //throw a C++ exception if theres a java exception         do_more_stuff(param); //might throw C++ exceptions         //prefer Java exceptions where possible:         if (condition1) throw NewJavaException(env, "java/lang/NullPointerException", "condition1");         //but C++ exceptions should be fine too         if (condition0) throw std::bad_alloc("BAD_ALLOC");         //YOUR CODE ABOVE THIS LINE.  HERES SOME RANDOM CODE      } catch(...) { //do not let C++ exceptions outside of this function         swallow_cpp_exception_and_throw_java(env);     }   } 

If you're really ambitious, it's possible to keep track of a StackTraceElement[] of your bigger functions, and get a partial stacktrace. The basic method is to give each function a StackTraceElement, and as they're called, push a pointer to them onto a thread-local "callstack" and when they return, pop the pointer off. Then, alter the constructor of NewJavaException to make a copy of that stack, and pass it to setStackTrace.

like image 142
Mooing Duck Avatar answered Oct 12 '22 02:10

Mooing Duck


Within the JNI literature, the word exception appears to be used exclusively to refer to Java exceptions. Unexpected events in native code are referred to as programming errors. JNI explicitly does not require JVMs to check for programming errors. If a programming error occurs, behavior is undefined. Different JVMs may behave differently.

It's the native code's responsibility to translate all programming errors into either return codes or Java exceptions. Java exceptions don't get thrown immediately from native code. They can be pending, only thrown once the native code returns to the Java caller. The native code can check for pending exceptions with ExceptionOccurred and clear them with ExceptionClear.

like image 24
Rob Kennedy Avatar answered Oct 12 '22 00:10

Rob Kennedy