Search code examples
javajvmjava-native-interfacejvmti

JNI GetObjectClass with primitive type


I have the following JNI/JVMTI code:

jfieldID field = ...;
jobject fieldValue = (*jni_env)->GetObjectField(jni_env, jObjectInstance, field);
jclass fieldClass = (*jni_env)->GetObjectClass(jni_env, fieldValue);
char* signature_ptr;
char* generic_ptr;
(*jvmti_env)->GetClassSignature(jvmti_env, fieldClass, &signature_ptr, &generic_ptr);

This code works as long field is a reference type (java.lang.Object and subtypes). If field is a primitive type, it will crash the JVM when it tries to execute GetObjectClass.

My question is: How can I use field and fieldValue to figure out if it is a primitive type and if it's a primitive type, which one (int, long, boolean etc.)?

I am new to JNI/JVMTI so if the answer is obvious, please bear with me.

Thanks!


Solution

  • It's illegal to call GetObjectField with jfieldID which is not an object type. For primitive fields, you need to call one of the other Get[Type]Field functions, where [Type] is Int/Long/Boolean/etc.

    Since you already have jvmti_env, the easiest way to find jfieldID type is to call JVM TI GetFieldName function. Among other things, it returns the field signature, i.e. the type of the field.

    The only problem is that jfieldID should always be accompanied with jclass - the holder of the field, since field IDs are relative to the particular class. Typically, when you get jfieldID, you already know its holder class. If not, in your case you have jObjectInstance - an object that has this field, so the holder class can be derived from the instance using JNI GetObjectClass function.

    jclass fieldHolder = (*jni_env)->GetObjectClass(jni_env, jObjectInstance);
    
    char* name;
    char* signature;
    if ((*jvmti_env)->GetFieldName(jvmti_env, fieldHolder, field, &name, &signature, NULL) == 0) {
    
        switch (signature[0]) {
            case 'I':
                // int
                break;
            case 'J':
                // long
                break;
            ...
            case 'L':
                // object
                break;
            case '[':
                // array
                break;
        }
    
        (*jvmti_env)->Deallocate(jvmti_env, signature);
        (*jvmti_env)->Deallocate(jvmti_env, name);
    }