Exception Handling: how can I catch C++ exception in Java [duplicate] - java

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.

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.

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.

I would label that as undefined behavior. Propagation of exceptions back to C code (that's what is running the JVM) is undefined behavior.
On Windows, compilers have to use Microsoft's Structured Exception Handling to implement exceptions, so C++ exceptions will be "safely" caried through C code. However, that C code is not written with exceptions in mind, so you will get a crash if you're lucky, and inconsistent state and resource leaks if you aren't.
On other platforms, well, I don't know, but it can't be any prettier. When I write JNI code, I wrap every C++ function in a try block: even if I don't throw, I still might get some of the standard exceptions (std::bad_alloc comes to mind, but others are possible too).

JNI uses c functions to interface with native code. C cannot handle exceptions correctly since it is not aware of their existence. So you have to catch the exceptions in your Native code and convert them to java exceptions or your jvm will crash. (This works since the java exception is only thrown once the native code returns to java)

I am guessing your JVM will crash. Native C++ exceptions do not propagate into Java through JNI. One reason for that is that JNI is a C interface, and C knows nothing of C++ exceptions.
What you have to do is catch the C++ exceptions before you get into the C layer of your JNI code, and make the JNI C function return an error code. Then you can check for the error code inside Java and throw a Java exception if necessary.

Related

Can I create a c++ class instance and use it as an entity in Android JNI?

Let's say I create a class 'Car' in cpp.
I want to creat an instance of that class with it's empty constructor in cpp.
Can I do it and use it in java code on android?
For instance:
Java code
Car myCar = new Car();
CPP class
class Car{
std::string model;
int creationYear;
Car(){}
}
thanks for the help
Yes. You can easily have a native object that shadows a Java object - assuming you can call the C++ Car() constructor. You could use a public static method in the C++ Car class to do that.
It's a bit of a hack, but a Java long is guaranteed to be 64 bits, so it's long enough to hold a native pointer value.
In Java:
public class Car
{
// A Java long is 64 bits, so it will
// hold a native pointer
private long nativeCar;
private native long newNativeCar();
private native void deleteNativeCar( long car );
public Car()
{
this.nativeCar = newNativeCar();
}
// allow for explicit native cleanup by caller
public synchronized deleteCar()
{
if ( 0 != this.nativeCar )
{
deleteNativeCar( nativeCar );
nativeCar = 0;
}
}
// handle cases where the native cleanup code
// was not called
#Override
protected void finalize() throws Throwable
{
deleteCar();
super.finalize();
}
}
Compile that, then use javah on the class file to create your C header file. (Note that JNI uses C, not C++. You can write C++ code to implement your logic, but the interface presented to the JVM must be a C interface.)
You'll get a couple of functions in your native header, something like this (I've stripped off the annotation from javah - you will need to keep that...):
jlong some_class_path_newNativeCar( JNIEnv *, jobject );
void some_class_path_deleteNativeCar( JNIEnv *, jobject, jlong );
You can implement your C++ code then:
jlong some_class_path_newNativeCar(
JNIEnv *env, jobject obj )
{
Car *nativeCar = new Car();
// C cast - we're returning a C value
return( ( jlong ) nativeCar );
}
void some_class_path_deleteNativeCar(
JNIEnv *env, jobject obj, jlong jNativeCar )
{
Car *cppNativeCar = ( Car * ) jNativeCar;
delete cppNativeCar;
}
I've deliberately kept the code simple - there's quite a bit I left out. javah can be a bit tricky to figure out how to use properly, for example. But if you can't figure out how to use javah properly you shouldn't be writing JNI code anyway.
Because JNI code is fragile. You can't make any mistakes, or you will wind up getting seemingly random failures or having your JVM crash, and the crash will likely not be easily traced to the bad code that caused the problem. There are lots of rules for making JNI calls using the JNIEnv * pointer supplied to a native call. For example, in general you can't save the values passed to you by the JVM and use them outside of the context you received them in - using them in another thread, or after the function where they were passed to you returns is a great way to cause those JVM crashes I mentioned above.
Nor can you make any JNI calls to Java if there are any exceptions pending from previous calls - again, you risk unpredictable errors and crashes if you do.
So keep your native code simple.
Keep all the logic and processing on only one side of the Java/native interface if you can.
Pass only C or Java strings, primitives or primitive arrays across the Java/native interface if you can.
Interacting with actual complex Java object from native code will take many, many lines of C or C++ code to safely replicate what can be done in Java with one or two lines of code. Even simple one-line get*()/set*() Java calls become 20 or 30 lines or more of C or C++ code if you replicate all the exception and failure checks you need in order to guarantee safe JVM execution after the native call no matter what data gets passed in. If you pass null to a Java method that can't handle a null value it will throw a NullPointerException that you can catch and your JVM runs happily or shuts down cleanly with an uncaught exception. Pass NULL to a JNI function that can't handle it, and if you don't properly check for any failure or any exception and then properly handle it, your JVM will exhibit seemingly unrelated failures or just crash.

Which Java Errors and Exceptions may (not) be thrown by "empty statements"?

Which subclass(es) of java.lang.Throwable may be thrown by an empty statement?
By the phrase "an empty statement", I'm referring to the "nothing", the "semi-colon", and the "semi-colons":
// ....
A(); B(); C();
try {
// nothing
} catch (java.lang.Throwable e) {
// which Throwable subclass might we see?
}
D(); E(); F();
try {
; // semi-colon
} catch (java.lang.Throwable e) {
// which Throwable subclass might we see?
}
G(); H(); I();
try {
; ; ;; ;;;;; ; ; ;;; ;; ;; ;; ;; ; ;; ; ;; // ... semi-colons
} catch (java.lang.Throwable e) {
// which Throwable subclass might we see?
}
J(); K(); L();
// ....
Which subclasses of Throwable may be thrown between A(); and B(); or between C(); and D(); or between F(); and G(); or between I(); and J();?
Or rather, which subclasses of Throwable are guaranteed not to appear between those statements?
The ones I know so far is the InternalError, the OutOfMemoryError, the StackOverflowError, and the UnknownError.
The compiler will probably remove the code that contains "nothing" or empty statements from the bytecode. The equivalent in the bytecode would be exactly similar to:
// ....
A(); B(); C();
D(); E(); F();
G(); H(); I();
J(); K(); L();
// ....
Of course during the execution, any kind of unexpected Error (like UnknownError) could occur and it's normally not expected to handle it in your application.
The closest kind of exception that could occur anywhere (emphasized in order to possibly cover the time between two bytecode instructions) is asynchronous exceptions:
Most exceptions occur synchronously as a result of an action by the thread in which they occur, and at a point in the program that is specified to possibly result in such an exception. An asynchronous exception is, by contrast, an exception that can potentially occur at any point in the execution of a program.
Asynchronous exceptions occur only as a result of:
An invocation of the (deprecated) stop method of class Thread or ThreadGroup.
The (deprecated) stop methods may be invoked by one thread to affect another thread or all the threads in a specified thread group. They are asynchronous because they may occur at any point in the execution of the other thread or threads.
An internal error or resource limitation in the Java Virtual Machine that prevents it from implementing the semantics of the Java programming language. In this case, the asynchronous exception that is thrown is an instance of a subclass of VirtualMethodError.
But again, there is no point to care about this type of exceptions (subclasses of VirtualMethodError) because they represent a serious error in the JVM execution. For example, it may be due to a manual interruption by the user using Ctrl+C. In this case, there's not much you can do about it.
If you don't execute any instruction then the VM is unlikely to request memory or run out of stack space for the current thread. As the other exceptions could be thrown because any kind of state in the VM is out of kilt, I guess you should always expect InternalError or UnknownError to occur. Hence you should not catch Throwable but Exception as it is unlikely you can recuperate from the error - unless you are creating your own framework maybe.
This question is very similar to the other question you posted. I think I'll try to address both questions here.
Since you refer to the JVMS I'll assume that you're after a formal answer, and the formal answer is that your question(s) doesn't really make sense. :-)
Asking how the JVM will execute a snippet of Java source code is like asking a mathematician the correct way of computing 10+10. The mathematician will probably say something like "how to compute it is not defined". Similarly, the JLS that defines the meaning of the Java snippet does not go into specifics of how to execute it.
So, first let me formalize your question slightly: "Where in the bytecode (emitted by the reference implementation of javac) corresponding to the given Java snippets could VirtualMachineErrors occur?"
This question is arguably much simpler to answer. The relevant section of the JVMS says
A Java Virtual Machine implementation throws an object that is an instance of a subclass of the class VirtualMethodError when an internal error or resource limitation prevents it from implementing the semantics described in this chapter. This specification cannot predict where internal errors or resource limitations may be encountered and does not mandate precisely when they can be reported.
Thus, the answer is: Between any two bytecode instructions.
Now to return to your original question: This snippet for instance
try {
// nothing
} catch (java.lang.Throwable e) {
// which Throwable subclass might we see?
}
is compiled to the empty program, which can't reasonably throw any exceptions.
Regarding your follow up question in a comment:
Should JLS 11.1.3 be read as "subclasses of Throwable are guaranteed not to appear between bytecode unless it is a subclass of VirtualMachineError"?
Yes, you could put it like that. I would perhaps have worded it a bit differently: Any instruction can give rise to
the exceptions specified by the JVM Instruction set for the instruction in question,
any exception of type VirtualMachineError
and no other exceptions

What does a Java method return to a JNI caller when it throws an exception?

Say I have the following Java code:
public class Test {
public static int foo() {
throw new RuntimeException();
}
}
which loads a native library in the usual manner. The native library registers and caches the JVM or whatever, and then, later on, this function gets executed:
JNIEnv* sEnv; // initialised somewhere properly
void throwMeARiver() {
jclass c = sEnv->FindClass("Test");
jmethodID m = sEnv->GetStaticMethodID(c, "foo", "()I");
jint i = sEnv->CallStaticIntMethod(c, m);
printf("Got %d\n", (int)i);
}
Evidently sEnv->CheckException() will now return JNI_TRUE, but what will the native function print out? In Java, throwing an Exception makes the JVM stop executing the method it's in until it finds an appropriate handler, so foo()'s return value is undefined. So is i also undefined?
I can't find any specs or anything saying otherwise, so presumably i /is/ undefined. There's no 'normal' range of CallStaticIntMethod in this case, unlike most JNI functions.
I'm trying this now, but I'm mainly asking this to see if there's a mandated behaviour somewhere.
While the variable i will be defined, its value is undetermined. In this situation, most likely, the value will be 0, but only if the JVM initialized the value in its implementation. Otherwise, it will be equal to whatever the data is present at its location in memory.
Its up to the implementer of the native function to properly check for an exception following a CallXXXMethod() via env->CheckException(), env->ExceptionOccurred(), and env->ExceptionDescribe(). Finally, a call to env->ClearException() to remove the exception.
No, JNI CheckException() is not equivalent to Java try … catch, therefore your new RuntimeException() will propagate further and maybe not get caught at all.
Thanks to #EJP for correcting me: actually, any Throwable from Java is intercepted by the C/C++ caller. From my tests on Android, the value returned is whatever happens to be in some unpredictable memory location (stack?).
For more discussion of JNI and exceptions, see How to catch JNI/Java Exception.

How is exception handling implemented in Java?

How is exception handling implemented in higher-level programming languages (like Java)? By this, I don't mean how to use exceptions within a language; I mean how the compiler generates code (assembly, or some intermediate, like Java byte code) that we recognize as exception-handling, because in the end, the computer can execute only instructions; Everything of a higher-level must be comprised of those instructions.
In C, before exceptions existed, you would return an error code, but if a function already returns something, then what? Perhaps return a structure of both the error code and the real result?
The compiler outputs an athrow instruction (JVM Specification) at the throw site, and an Exceptions attribute (#4.7.5) in the code of the method that shows where all the various catch clauses are, what range of instructions they cover, and what exception types they catch.

JNA Catching Exceptions

I have a quick question about dealing with exceptions being thrown by libraries under JNA...
When I throw an exception in the underlying native code, JNA gets a invalid memory access error. I'm assuming this is because C libraries cannot throw an exception up through it's stack (it's actually C++/CLR but has C exports)? So is there no real way to report the exception to Java? Or "should it work" and I'm just doing something incredibly wrong?
DllExport void Initialize(char* dir)
{
throw gcnew System::Exception("Testing");
}
It would be nice for Java to be able to detect these thrown exceptions, and I guess I could actually look into passing a memory pointer into all my C exports and check to see if those are null or not, but seems like a roundabout way.
C++ exceptions can only be handled in C++ code. They should never be allowed to escape the C++ world (i.e., a C interface of C++ code should never let exceptions propagate). It is not even safe to let a C++ exception propagate through a layer of C code between two C++ modules (e.g., when a C++ function calls a C function which in turn calls a C++ function).
One of the reasons for this is that there is no standard on how C++ exceptions should be implemented, so C++ modules are only binary-compatible if compiled by the same compiler (in the same version). So code in any other language can't be set up to handle C++ exceptions.
In this case (C++ library, C interface, called from Java) you would have to catch the C++ exception, propagate the information through the C interface (e.g., by using error return codes), check for it in Java and throw an exception there.
You need to handle the c++ exception yourself and instead build a java exception which can be passed to the java side of the code.

Categories

Resources