tags:

views:

796

answers:

3

Hi,

I have some native code which returns a jbyteArray (so byte[] on the Java side) and I want to return null. However, I run into problems if I simply return 0 in place of the jbyteArray.

Some more information: The main logic is in Java, the native method is used to encode some data into a byte stream. don;t ask.. it has to be done like this. Recently, the native code had to be changed a bit and now it runs horribly horrible slow. After some experimentation, which included commenting out all code in the native method before the return, it turns out that returning 0 causes the slowdown. When returning an actual jbyteArray, everything is fine.

Method signatures for my code:

On the C++ side:

extern "C" JNIEXPORT jbyteArray JNICALL Java_com_xxx_recode (JNIEnv* env, jclass java_this, jbyteArray origBytes, jobject message)

On the Java side:

private static native byte[] recode(byte[] origBytes, Message message);

The native code looks something like this:

jbyteArray javaArray;
if (error != ERROR) {
    // convert to jbyteArray
    javaArray = env->NewByteArray((jsize) message.size);
    env->SetByteArrayRegion(java_array, 0, message.size, reinterpret_cast<jbyte*>(message.buffer()));
    if (env->ExceptionOccurred()) {
        env->ExceptionDescribe();
        error = ERROR;
    }
}
if (error == ERROR) {
    return 0; // Does NOT work - doesn't crash, just slows everything down horrible.
}
else {
    return javaArray; // Works perfectly.
}

Does anyone know of any reasons that this could happen? Is it valid to return NULL from a native method in place of a jbyteArray, or is there another procedure to return null back to Java. Unfortunately, I had no luck on Google.

Thanks!

EDIT: Added additional information.

+2  A: 

I do not like the solution to return NULL. If possible you should return some Empty implementations like

return new ArrayList();
return new byte[0];
return new EmptySomeObjectImpl();

Than you do not have worried about NullPointerExceptions

if (error == ERROR) {
    return env->NewByteArray(0);
}

EDIT

Try to return this always if you dont want to implement the EmptyObjects.

if (error == ERROR) {
     return javaSrray; // Please test it and than return javaSrray always without if else
}
else {
    return javaSrray; // Works perfectly.
}
Markus Lausberg
But what if I want Java to get a "null"? What is the correct procedure for this using JNI? Is there some jnull object which I've overlooked, or..?
Dan
Why you are asking for null. When you return an empty object you can remove the NULL clase and use your normal logic.
Markus Lausberg
Two reasons: 1) I'd like to avoid rewriting existing code and, more importantly, 2) because it must be possible and I'd like to learn how. If I cannot figure it out, then I will use your method though. Thanks.
Dan
A: 

There's some asymmetry in your code that struck my eye: you never decide upon the type of object to return, except when returning 'nothing'. Apparently the env object decides how to allocate a javaSrray, so why not ask it to return some kind of empty array? It may be possible that the 0 that you return needs to be handled in a special way while marshaling between jni and java.

xtofl
Yes, I assumed that instead of returning 0 (which appears to WORK, ie I get a null back on the Java side, but for some strange strange reason its really slow.. maybe JNI treats it as an error case and returns null because of this as a side effect? and the additional error handling is slowing it down??) that I should be returning some kind of jnull object, but I had no look actually finding one...
Dan
+1  A: 

Have you tried returning a NULL reference?

This is untested (don't have a JNI development environment at hand at the moment) but you should be able to create a new global reference to NULL and return it like this:

return (*env)->NewGlobalRef(env, NULL);

EDIT That being said, you check if an exception occurs, but do not clear it. That, as far as I can understand, means that it is still "thrown" in the Java layer, so you should be able to use just that as an error indicator; then it does not matter what the function returns. In fact, calling a JNI function other than ExceptionClear()/ExceptionDescribe() when an exception is thrown is not "safe" according to the documentation. That the functions is "slow" might be caused by the ExceptionDescribe() function writing debugging information.

So, if I understand this correctly, this should be a well-behaved function throwing an exception the first time an error occurs, and returning NULL on each subsequent call (until 'error' is cleared):

if (error != ERROR) {
    jbyteArray javaArray = env->NewByteArray((jsize) message.size);
    env->SetByteArrayRegion(javaArray, 0, message.size, reinterpret_cast<jbyte*>(message.buffer()));
    if (env->ExceptionOccurred()) {
        error = ERROR;
        return 0;
    }
    return javaArray;
} else {
    return env->NewGlobalRef(NULL);
}

Again, this is untested since I dont have a JNI environment available right now.

Christoffer
Thanks for that. Well, the code is still slow if everything is commented out EXCEPT the return 0, so it can't be the call to ExceptionDescribe(). Still a good point though and your code IS a bit cleaner than mine.
Dan
What VM do you use? If it's an open-sourced one, see if you can find out if there's some extra validation step or whatever if a native function returns null :) Perhaps it's better to raise an exception in the native code for the 'else' case as well?
Christoffer
No, using the Sun VM. Yeah, I'm assuming that theres additional error handling code being run when the return value is zero. Anyway, it would be nice to know what the deal here is, but I can fix it in other ways anyway.
Dan