Invalid indirect reference on NewObject call
Asked Answered
A

1

7

OK, so I have the native code below. I'm trying to return an array of FilePermissionInfo from it, populated with some data returned by stat(). The problem is that I get the following error when NewObject is called the first time:

06-15 20:25:17.621: W/dalvikvm(2287): Invalid indirect reference 0x40005820 in decodeIndirectRef 06-15 20:25:17.621: E/dalvikvm(2287): VM aborting

It's odd, because the only reference object I have is the jclass (for FilePermissionInfo) and I turn it to a global reference.

The code is:

JNIEXPORT jobjectArray JNICALL
Java_com_mn_rootscape_utils_NativeMethods_getFilesPermissions( JNIEnv* env, jobject thizz, jobjectArray filePathsArray ) 
{
jobjectArray result;
int size = (*env)->GetArrayLength(env, filePathsArray);
jboolean isCopy;

jclass filePermInfoCls = (*env)->FindClass(env, kFilePermissionInfoPath);
if(!filePermInfoCls)
{
    LOGE("getFilesPermissions: failed to get class reference.");
    return NULL;
}

gFilePermInfoClass = (jclass)(*env)->NewGlobalRef(env, filePermInfoCls);
LOGI("got gFilePermInfoClass");

jmethodID filePermInfoClsConstructor = (*env)->GetMethodID(env, gFilePermInfoClass, "<init>", kFilePermInfoConstructorSig);
if(!filePermInfoClsConstructor)
{
    LOGE("getFilesPermissions: failed to get method reference.");
    return NULL;
}

struct stat sb;

LOGI("starting...");
result = (jobjectArray)(*env)->NewObjectArray(env, size, gFilePermInfoClass, NULL);
for(int i = 0; i != size; ++i) 
{
    jstring string = (jstring) (*env)->GetObjectArrayElement(env, filePathsArray, i);
const char *rawString = (*env)->GetStringUTFChars(env, string, &isCopy);    

    if(stat(rawString, &sb) == -1) 
    {
        LOGE("stat error for: %s", rawString);
    }

    LOGI("%ld %ld %ld %ld %ld %ld %ld %ld", sb.st_dev, sb.st_mode, sb.st_nlink, sb.st_uid, sb.st_gid, sb.st_atime, sb.st_mtime, sb.st_ctime);

    jobject permInfo = (*env)->NewObject(env, 
                            gFilePermInfoClass, 
                            filePermInfoClsConstructor, 
                            (long)sb.st_dev,
                            (long)sb.st_mode,
                            (long)sb.st_nlink,
                            (long)sb.st_uid,
                            (long)sb.st_gid,
                            (long)sb.st_atime,
                            (long)sb.st_mtime,
                            (long)sb.st_ctime,
                            "",
                            "",
                            1,
                            "");

    LOGI("xxx1");
    (*env)->SetObjectArrayElement(env, result, i, permInfo);
    LOGI("xxx2");
    (*env)->ReleaseStringUTFChars(env, string, rawString);
    LOGI("xxx3");
}

(*env)->DeleteLocalRef(env, filePermInfoCls);

return result;

}

The Java class constructor signature and path are:

const char* kFilePermissionInfoPath = "com/mn/rootscape/utils/FilePermissionInfo";
const char* kFilePermInfoConstructorSig = "(JJJJJJJJLjava/lang/String;Ljava/lang/String;ZLjava/lang/String;)V";

Please note that if I call NewObject on the default constructor then it works fine.

Aimee answered 15/6, 2012 at 17:38 Comment(1)
I really hope this isn't a bug in the NDK (v8). I've tried all sorts of approaches and it's very weird that it works with the default constructor (i.e. "()V". Ultimately I can use the setters in that class to set the values, but I wouldn't like invoking too many methods over the JNI boundary.Aimee
A
13

OK, found it. It was a problem with the jstring parameters. It turns out you cannot pass empty strings (or even NULL for that matter) as a jstring. Instead I used (*env)->NewStringUTF(env, NULL) to create a NULL jstring.

Seems to work OK now.


Since this question generated somewhat a high activity, I'm posting the final solution below. Note that the nullString variable is being deallocated at the end of its scope (or when you're done using it):

        jstring nullString = (*env)->NewStringUTF(env, NULL);
...
        jobject permInfo = (*env)->NewObject(env, 
                                gFilePermInfoClass, 
                                filePermInfoClsConstructor, 
                                (jbyte)permsOwner,
                                (jbyte)permsGroup,
                                (jbyte)permsOthers,
                                (jlong)sb.st_uid,
                                (jlong)sb.st_gid,
                                (jlong)sb.st_atime,
                                (jlong)sb.st_mtime,
                                (jlong)sb.st_ctime,
                                nullString,
                                nullString,
                                (jboolean)1,
                                nullString);
...
       (*env)->DeleteLocalRef(env, nullString);
Aimee answered 16/6, 2012 at 21:1 Comment(3)
Do you mean that you replaced the "","", arguments in (*env)->NewObject(...) function call into a jstring myNullString = (*env)->NewStringUTF(env, NULL) then pass myNullString as arguments?Diandrous
I'm not sure that this is still a problem with empty jstrings, as originally stated. I was running into this issue, but was able to fix it using something like: std:string empty(""); jstring myemptyString = (*env)->NewStringUTF(empty.c_str()); Note that I was testing with android 4.3.Loire
@MichaelMarsella: I think it's just about passing a non-NULL value. Doesn't seem to care if the underlying buffer is actually NULL or an empty string. However, cannot say for sure if the behavior has changed in 4.3 or 4.4 as I haven't been able to test it.Aimee

© 2022 - 2024 — McMap. All rights reserved.