kotlinjava-native-interface

Enums in JNI GetMethodID signature parameter list


So, this is an edge case that no amount of Googling seems to unearth.

I have an enum in Kotlin;

package myapp
public enum State {
    STATE_SUCCEEDED,
    STATE_FROZEN,
    STATE_WAITING,
    STATE_IN_PROGRESS,
    STATE_FAILED
}

I then have an object that contains this enum as a value in a member variable

package myapp
class AppState (
    val state: State
)

Then, in C, I want to create an instance of AppState

jclass cls = (*env)->FindClass(env, AppState_ClassPath);
if (cls == NULL) {
    __android_log_print(ANDROID_LOG_INFO, "DEBUG", "AppState class is null");
}
jmethodID mid = (*env)->GetMethodID(env, cls, "<init>", "(Lmyapp/State;)V");
return (*env)->NewObject(
    env, 
    cls, 
    mid, 
    enumState
);

Now, when the C code is called from Kotlin, I get the error

JNI DETECTED ERROR IN APPLICATION: JNI NewStringUTF called with pending exception java.lang.NoSuchMethodError: no non-static method "Lmyapp/AppState;.<init>(Lmyapp/State;)V"

I understand why I get this error, since Enums have no public constructor, but not sure of the workaround. How should this enum be added to the signature?

Many thanks!

EDIT: (output from javap)

public final class myapp.AppState {
  public myapp.AppState(myapp.State);
    descriptor: (Lmyapp/State;)V

  public final myapp.State getState();
    descriptor: ()Lmyapp/State;
}

EDIT 2: (enum conversion function)

jobject
getState(JNIEnv* env, enum State state) {
    char path[64];
    char* stateName;

    switch(state) {
        case STATE_SUCCEEDED:
            stateName = "STATE_SUCCEEDED";
            break;
        case STATE_FROZEN:
            stateName = "STATE_FROZEN";
            break;
        case STATE_WAITING:
            stateName = "STATE_WAITING";
            break;
        case STATE_IN_PROGRESS:
            stateName = "STATE_IN_PROGRESS";
            break;
        default:
            stateName = "STATE_FAILED";
    };

    snprintf(path, sizeof(path) - 1,"[L%s;", State_ClassPath);
    jclass cls = (*env)->FindClass(env, State_ClassPath);
    jfieldID fid = (*env)->GetStaticFieldID(env, cls , stateName, path);
    jobject stateObj = (*env)->GetStaticObjectField(env, cls, fid);
    return stateObj;
}

Solution

  • The answer turned out to be an erroneous [ in the code. Using javap -s and copying / pasting from the output is the safest way to avoid this issue.