JNIEnv->GetMethodID leads a crash - java-native-interface

jclass clazz_obj = jni_env->GetObjectClass(klazz);
jmethodID mid = jni_env->GetMethodID(clazz_obj, "getName", "()Ljava/lang/String;");
This is the stacktrace:
V [libjvm.so+0x74e290] JvmtiRawMonitor::is_valid()+0x0
C [libVWave.so+0x73cd0] JNIEnv_::GetMethodID(_jclass*, char const*, char const*)+0x3c
I want to get the full name of a class. However, jvmti doesn't have this API. I try to do this in MethodEntry CallBack to get the full name by JNI.

Related

ReleaseStringUTFChars with non-original jstring argument?

In order to minimize JNI marshalling, I want to store some strings on the C++ side as static variables via a setup method, use them in a different JNI method call rather than passing them each time, and then release the strings later with yet another JNI method call. For example,
C++ code:
static const char *stringValue1;
extern "C" JNIEXPORT void JNICALL
Java_mypackage_myclass_setValue1(JNIEnv* env, jclass jobj, jstring javaString) {
jboolean isCopy;
if(!env->IsSameObject(javaString, NULL)) {
const char *stringValue1= env->GetStringUTFChars(javaString, &isCopy);
}
}
extern "C" JNIEXPORT void JNICALL
Java_mypackage_myclass_execute(JNIEnv* env, jclass jobj, jint javaInt) {
// ... use the stringValue1 static variable - otherwise, this method would need another jstring argument.
}
extern "C" JNIEXPORT void JNICALL
Java_mypackage_myclass_releaseValue1(JNIEnv* env, jclass jobj, jstring javaString) {
if(stringValue1 != 0) {
env->ReleaseStringUTFChars(javaString, stringValue1);
}
}
Java code:
myclass.setValue1("hello")
for(int i = 0; i < 10; i++) {
myclass.execute(i); // execute needs the "hello" string, but we try to store it in the C++ side rather than pass it in each loop iteration.
}
myclass.releaseValue1("hello");
Is this a valid JNI pattern? In all of the JNI examples I have seen, both calls to GetStringUTFChars and ReleaseStringUTFChars occur in the same method/scope. In this example, they do not occur in the same scope, and the jstring argument could possibly not be the same Java String either.
Yes, the documentation states
This array is valid until it is released by ReleaseStringUTFChars().
This is corroborated by the implementation in Hotspot, which just allocates off-heap memory for a copy.

How to convert QString to jstring?

I tried the following code to convert QString to jstring according to PaulMcKenzie's answer.
jstring CreateJStringFromQString(JNIEnv *env, const QString& str)
{
jstring js = env->NewString(reinterpret_cast<jchar *>(str.data()), str.size());
return js;
}
But , I have the following error
/home/runner/work/jni/jni/hello/src/main/cpp/HelloJNIImpl.cpp:46:69:
error: reinterpret_cast from type ‘const QChar*’ to type ‘jchar*’
{aka
‘short unsigned int*’} casts away qualifiers
46 | jstring js = env->NewString(reinterpret_cast<jchar *>
(str.data()), str.size());
^
FAILURE: Build failed with an exception.
I tried that answer . But it comes the above error.
How to convert QString to jstring ?
I don't use Qt, so I am going by the documentation here with regards to QString.
Given that, you can try the following:
jstring CreateJStringFromQString(JNIEnv *env, const QString& str)
{
jclass string_class = env->FindClass("java/lang/String");
jmethodID string_constructor = env->GetMethodID(string_class, "<init>", "(Ljava/lang/String;)V" );
jstring js = env->NewObject(string_class, string_constructor, env->NewString(str.data(), str.size()));
return js;
}
I use code similar to the above in the JNI interfaces I've written.
There is no error checking to see if the string_class and string_constructor are valid, but assume they are.
Basically you have to construct a java String object, and populate it with the data.
Since Qt 6.1 you can use QJniObject
If you need only temporary jstring e.g. to pass argumment to jni function call, then you can use such a conversion:
QJniObject::fromString(your_QString).object<jstring>()
But as mentioned in Qt documentation you need to be aware of string lifetime being bound to scope of owning QJniObject.

How to obtain the name of a Java class from its corresponding jclass?

I have a jclass and I need to find out the name of the corresponding Java class. There is a well-received answer on SO for a similar question, however, it requires an object that's an instance of this class: https://stackoverflow.com/a/12730789/634821
I don't always have an instance where having the name of a jclass would be very useful, and I find it hard to believe that Java has no way to find a class's name, which is obviously a static property, with only static methods. So, is it doable, and how?
A jclass is a jobject subclass that references a java Class object.
So, cut the first few lines off the answer you found:
jclass input = ...; // Class<T>
jclass cls_Class = env->GetObjectClass(input); // Class<Class>
// Find the getName() method on the class object
mid = env->GetMethodID(cls_Class, "getName", "()Ljava/lang/String;");
// Call the getName() to get a jstring object back
jstring strObj = (jstring)env->CallObjectMethod(input, mid);
// Now get the c string from the java jstring object
const char* str = env->GetStringUTFChars(strObj, NULL);
// Print the class name
printf("\nCalling class is: %s\n", str);
// Release the memory pinned char array
env->ReleaseStringUTFChars(strObj, str);

JNI Calling Java Method With Array Parameter

I am trying to call a java method from cpp. I seem to have no problem using strings, int, etc. One problem I am having those is passing an int array parameter over. Can someone tell me what I did wrong? I apologize if it is a very small error and I just totally missed it.
JNIEXPORT void JNICALL
Java_basket_menu_MenusActivity_submitInfo(JNIEnv *, jclass){
int placement[2] = { 5, 4 };
jclass cls = env->FindClass("basket/menu/MenusActivity");
jmethodID mid2 = env->GetStaticMethodID(cls, "PlaceMe", "([I)V");
env->CallStaticVoidMethod(cls, mid2, placement);
}
You need to create a jintArray and copy the contents of placement to it:
jintArray arr = env->NewIntArray(2);
env->SetIntArrayRegion(arr, 0, 2, placement);
env->CallStaticVoidMethod(cls, mid2, arr);
Refer to the documentation for more information about these functions.

How to access arrays within an object with JNI?

JNI tutorials, for instance this one, cover quite well how to access primitive fields within an object, as well as how to access arrays that are provided as explicit function arguments (i.e. as subclasses of jarray). But how to access Java (primitive) arrays that are fields within an jobject? For instance, I'd like to operate on the byte array of the following Java object:
class JavaClass {
...
int i;
byte[] a;
}
The main program could be something like this:
class Test {
public static void main(String[] args) {
JavaClass jc = new JavaClass();
jc.a = new byte[100];
...
process(jc);
}
public static native void process(JavaClass jc);
}
The corresponding C++ side would then be:
JNIEXPORT void JNICALL Java_Test_process(JNIEnv * env, jclass c, jobject jc) {
jclass jcClass = env->GetObjectClass(jc);
jfieldID iId = env->GetFieldID(jcClass, "i", "I");
// This way we can get and set the "i" field. Let's double it:
jint i = env->GetIntField(jc, iId);
env->SetIntField(jc, iId, i * 2);
// The jfieldID of the "a" field (byte array) can be got like this:
jfieldID aId = env->GetFieldID(jcClass, "a", "[B");
// But how do we operate on the array???
}
I was thinking to use GetByteArrayElements, but it wants an ArrayType as its argument. Obviously I'm missing something. Is there a way to to this?
I hope that will help you a little (check out the JNI Struct reference, too):
// Get the class
jclass mvclass = env->GetObjectClass( *cls );
// Get method ID for method getSomeDoubleArray that returns a double array
jmethodID mid = env->GetMethodID( mvclass, "getSomeDoubleArray", "()[D");
// Call the method, returns JObject (because Array is instance of Object)
jobject mvdata = env->CallObjectMethod( *base, mid);
// Cast it to a jdoublearray
jdoubleArray * arr = reinterpret_cast<jdoubleArray*>(&mvdata)
// Get the elements (you probably have to fetch the length of the array as well
double * data = env->GetDoubleArrayElements(*arr, NULL);
// Don't forget to release it
env->ReleaseDoubleArrayElements(*arr, data, 0);
Ok here I operate with a method instead of a field (I considered calling a Java getter cleaner) but you probably can rewrite it for the fields as well. Don't forget to release and as in the comment you'll probably still need to get the length.
Edit: Rewrite of your example to get it for a field. Basically replace CallObjectMethod by GetObjectField.
JNIEXPORT void JNICALL Java_Test_process(JNIEnv * env, jclass c, jobject jc) {
jclass jcClass = env->GetObjectClass(jc);
jfieldID iId = env->GetFieldID(jcClass, "i", "I");
// This way we can get and set the "i" field. Let's double it:
jint i = env->GetIntField(jc, iId);
env->SetIntField(jc, iId, i * 2);
// The jfieldID of the "a" field (byte array) can be got like this:
jfieldID aId = env->GetFieldID(jcClass, "a", "[B");
// Get the object field, returns JObject (because Array is instance of Object)
jobject mvdata = env->GetObjectField (jc, aID);
// Cast it to a jdoublearray
jdoubleArray * arr = reinterpret_cast<jdoubleArray*>(&mvdata)
// Get the elements (you probably have to fetch the length of the array as well
double * data = env->GetDoubleArrayElements(*arr, NULL);
// Don't forget to release it
env->ReleaseDoubleArrayElements(*arr, data, 0);
}
In gcc 6.3 I get a warning saying "dereferencing type-punned pointer will break strict-aliasing rules" from a line like this:
jdoubleArray arr = *reinterpret_cast<jdoubleArray*>(&mvdata);
But since jdoubleArray is itself a pointer to class _jdoubleArray, there's no need to get the address before casting, and this static_cast works without warnings:
jdoubleArray arr = static_cast<jdoubleArray>(mvdata);