Reputation: 3721
I have a JNI function that returns a UChar array (from the ICU4C library) which I'd like to convert to a Java character array so I can call this from Java. I am not sure where the problem is as whenever I access this JNI function, everything crashes and hangs but I get no error message anywhere, including in the logcat... very difficult to debug!
Can the UChar array map directly to a jcharArray type? also, can I use it as a return type? or could I pass it in as a parameter that the JNI function then populates?
Here is a snippet of basically what I am trying to do:
static jint testFunction(JNIEnv* env, jclass c, jobject obj, jcharArray chsArray,
int offset, int len, jcharArray dstArray) {
jchar* dst = env->GetCharArrayElements(dstArray, NULL);
if (dst != NULL) {
UChar *str = new UChar[len];
//populate str here from an ICU4C function
for (int i=0; i<len; i++)
dst[i] = str[i]; //this might be the problematic piece of code (can I issue an assignment like this?)
}
}
env->ReleaseCharArrayElements(dstArray, dst, 0);
}
Any help is appreciated!
Thanks
Upvotes: 0
Views: 987
Reputation: 5525
If your intent is to get a UChar* value from ICU and return a string to Java (I'm assuming this based on the "populate str here from ICU4C function" comment), why not just use a jstring?
For example:
jstring Java_com_mysomethingsomething_test_getAString(JNIEnv* env, jobject thiz)
{
UChar* buf = new UChar[BUF_LEN];
int32_t len;
PouplateBuffer(buf, &len); //populate str here from an ICU4C function
jstring result = env->NewString(reinterpret_cast<jchar*>(buf), static_cast<jint>(len));
delete [] buf;
return result;
}
The example is simplifed of course, but should illustrate UChar* to jstring conversion. This also easily works with a UnicodeString:
jstring Java_com_mysomethingsomething_test_getAString(JNIEnv* env, jobject thiz)
{
const UnicodeString result = PopulateString();
return env->NewString(reinterpret_cast<jchar*>(result.getBuffer()), static_cast<jint>(result.length()));
}
Upvotes: 1
Reputation: 64066
JNI can be a real headache. You function looks fine, on the surface.
First, I note that you are not using offset
- that's a code-smell.
Second, you are not freeing the UChar array.
Third, either the C function or the assignment loop may be overrunning the array bounds.
To assist with locating abrupt crashes like this, I have successfully used a good-ol-fashioned print
statement in conjunction with the console.
First I added a println method to my JNIGlobal class:
/** Print text or ASCII byte array prefixed with "JNI: ". Primarily for native code to output to the Java console. */
static public void println(Object val) {
if(val instanceof byte[]) { byte[] ba=(byte[])val; val=new String(ba,0,ba.length); }
System.out.println("JNI: "+val);
}
Then I added a corresponding method to my C code:
void println(JNIEnv *jep, byte *format,...) {
va_list vap;
byte txt[5001];
jsize txtlen;
jclass eCls;
jint mId;
jbyteArray jText;
va_start(vap,format); vsprintf(txt,format,vap); va_end(vap);
txtlen=(long)strlen(txt);
if((eCls=(*jep)->FindClass(jep,"<your/package/here/JNIGlobal"))==0) {
printf("JNI: Global class not found (Error Text: %s)\n",txt);
return; /* give up */
}
if((mId=(*jep)->GetStaticMethodID(jep,eCls,"println","(Ljava/lang/Object;)V"))==0) {
printf("JNI: Global println method not found (Error Text: %s)\n",txt);
return; /* give up */
}
jText=(*jep)->NewByteArray(jep,txtlen);
(*jep)->SetByteArrayRegion(jep,jText,0,txtlen,(void*)txt);
(*jep)->CallStaticVoidMethod(jep,eCls,mId,jText);
}
Then I just call println(env,"Some formatted output")
at each line in the source to see how far it gets. In my environment (an AS/400), when the JVM crashes during interative running I am left with the console - you might want to add a short delay in the Java code to ensure you see the output before your console goes away.
So for you, like so:
static jint testFunction(JNIEnv* env, jclass c, jobject obj,
jcharArray chsArray, int offset, int len, jcharArray dstArray) {
/**/println("** testFunction 1");
jchar* dst = env->GetCharArrayElements(dstArray, NULL);
/**/println("** testFunction 2");
if (dst != NULL) {
/**/println("** testFunction 3");
UChar *str = new UChar[len];
/**/println("** testFunction 4");
//populate str here from an ICU4C function
/**/println("** testFunction 5");
for (int i=0; i<len; i++)
dst[i] = str[i]; //this might be the problematic piece of code (can I issue an assignment like this?)
}
/**/println("** testFunction 6");
}
env->ReleaseCharArrayElements(dstArray, dst, 0);
/**/println("** testFunction 7");
}
Upvotes: 1
Reputation: 4350
ICU4JNI is not actively maintained, but you might be able to look at it for an example of calling ICU4C from JNI. See also ICU4JNI SVN trunk
Upvotes: 0
Reputation: 15656
How long is the dstArray? c++ cannot check array bounds and happily corrupts the memory of your process if len is larger than dstArray.length.
Upvotes: 0