Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JNI Invoking Static methods. Is the class object necessary?

Started using JNI to invoke static java methods from C++. Specifically, after getting a jclass (using FindClass) and a jmethodID (using GetStaticMethodID), I proceeded to call the series of CallStatic*MethodA routines. Turns out that all of these routines take a jclass as first parameter. I started wondering why the class object is needed: since all information was provided in the GetStaticMethodID, the class object seems unnecessary for the JVM to get the job done. I then tried calling these routines while passing NULL for the first parameter, and the invocation succeeded.

My question: Is it safe to call these methods with a NULL class object?

The incentive is: If it is indeed legal, I will not have to cache the class object for subsequent calls to the static methods (while remembering to call NewGlobalRef....). Caching the jmethodID would be sufficient.

like image 485
user4212919 Avatar asked Apr 23 '15 18:04

user4212919


2 Answers

No, it is absolutely NOT safe to call such static function with a null (or an invalid) class pointer.

Your practice may very well succed, for example if your static method doesn't refer to any other static class member. However, if your static java method refers to any other static member, your JVM will need the valid class pointer.

Example:

Take this simple Java demo MyTest.java:

public class MyTest {
    public static void mymain() {
        System.out.println("Hello, World in java from mymain");
        System.out.println(magic_counter);   // this will cause a segfault if 
    }                                        // class pointer is null 
    private static int magic_counter=777; 
}

And call it with the following JNI C++ snippet

... // JVM already loaded and initialised

jclass cls2 = env->FindClass("MyTest");
if(cls2 == nullptr) {
    cerr << "ERROR: class not found !";
}
else {
    cout << "Class MyTest found" << endl; 
    jmethodID mid = env->GetStaticMethodID(cls2, "mymain", "()V"); 
    if(mid == nullptr) 
        cerr << "ERROR: method void mymain() not found !" << endl; 
    else {
        env->CallStaticVoidMethod(cls2, mid);
        cout << endl;
    }
 }

Calling GetStaticMethodID(nullptr, "mymain", "()V"); would fail. Because when mymain() executes, it will try to get access to the static variable magic_number. The JVM will then use the class pointer you have provided and assume it's a vaild pointer returned by a class loaded. But as it is null, the system will segfault.

like image 200
Christophe Avatar answered Nov 15 '22 21:11

Christophe


NO you shouldn't do this, but its important to understand why you can in some cases use NULL for the class when calling a static method.... At least in some implementations. I'm certainly no expert on java or the jni, but if you read the source code at https://android.googlesource.com/platform/art/+/master/runtime/jni/jni_internal.cc its pretty clear that the android jni does not reference the object parameter for the CallStatic* methods...

static jboolean CallStaticBooleanMethod(JNIEnv* env, jclass, jmethodID mid, ...)

The only problem is you are not guaranteed that behavior on all platforms, this is just the source from one possible implementation.

https://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/functions.html#wp15982 mentions that the method must come from clazz so its quite possible other implementations make a secondary check before the call is made, even though this is a performance trade off a release implementation of java would probably not make.

Its worth noting that a static method can only access static members and therefore this call should be safe, however the jclass object is not an instance of the class it is the class definition, which could easily be used for diagnostic information or something else unrelated to actually calling the method. Be safe and store the jclass object, its really no overhead to do so as its not an actual instance of the object.

like image 31
Medran Avatar answered Nov 15 '22 21:11

Medran