Ayyoudy
Ayyoudy

Reputation: 3721

JNI - mapping UChar type to

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

Answers (4)

NuSkooler
NuSkooler

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

L. Cornelius Dol
L. Cornelius Dol

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

Steven R. Loomis
Steven R. Loomis

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

josefx
josefx

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

Related Questions