Skip to content

Commit

Permalink
Increase test coverage to 93%
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexanderSchuetz97 committed Sep 21, 2024
1 parent bec134b commit 22c8a9a
Show file tree
Hide file tree
Showing 9 changed files with 726 additions and 124 deletions.
25 changes: 17 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,22 +158,31 @@ This feature enables assertions in the code. This is useful for debugging and te
These checks will cause a big performance hit and should not be used in production builds.

I would not even recommend using this feature for normal debugging builds
unless you are specifically debugging issues that occur in relation to the JNI interface.
There is no need to enable this feature when you are just debugging a problem that occurs in pure rust.
unless you are specifically debugging issues that occur in relation to UB with the JNI interface.
There is no need to enable this feature when you are just debugging a problem that occurs in pure rust code.

Enabling this feature for automated unit/integration tests that perform complicated JNI calls can be a good idea if you
can tolerate the performance penalty of enabling this feature.
Enabling this feature for benchmarks is not recommended as it falsifies the results of the benchmark.

This feature should NOT be used with the jvm launch option `-Xcheck:jni`
as the assertions contain calls to `env.ExceptionCheck()` which will fool the JVM into thinking
that your user code checks for exceptions, which it may not do.
as the assertions contain calls to `env.ExceptionCheck()` before nearly every normal call to the jvm,
which will fool the JVM into thinking that your user code checks for exceptions, which it may not do.
It will also generate many false negatives as some 'assertion' code does not call `env.ExceptionCheck()`
as the only realistic scenario for some calls that are made to fail is for the JVM to run out of memory.

I recommend using this feature before or after you have tested your code with `-Xcheck:jni` depending
on what problem your troubleshooting. The assertions are generally much better at detecting things like null pointers
or invalid parameters than the JVM checks, while the JVM checks are able to catch missing exception checks or JVM Local Stack overflows better.

Since asserts are implemented using unwinding panics the panics can be caught.
It is not recommended to continue or try to "recover" from this as the
assertions do NOT perform cleanup actions when a panic occurs so you will leak JVM memory.
I recommend aborting the processes on such a panic as such a panic only occurs if the rust code had triggered UB in the JVM.
The asserts are implemented using rust panics.
If you compile your project with unwinding panics the panics can be caught.
It is not recommended to continue or try to "recover" from these panics as the
assertions do NOT perform cleanup actions when a panic occurs, so you will leak JVM locals or leave the JVM in an
otherwise unrecoverable state. I recommend aborting the processes on such a panic as such a panic only occurs,
if the rust code had triggered UB in the JVM in the absence of the assertions.
This can either be done by calling abort when "catching" the panic or compiling your rust code with panic=abort
if you do not need to catch panics anywhere in your rust code.

### Further Info
### Variadic up-calls
Expand Down
Binary file modified java_testcode/FieldTests.class
Binary file not shown.
Binary file modified java_testcode/MethodCalls$NvChild.class
Binary file not shown.
Binary file modified java_testcode/MethodCalls.class
Binary file not shown.
Binary file added java_testcode/RegisterTest.class
Binary file not shown.
15 changes: 15 additions & 0 deletions java_testcode/RegisterTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import java.lang.System;

public class RegisterTest {
public static native void test(String abc);

public static native void test(double abc);

public static void callTest(String abc) {
test(abc);
}

public static void callTest(double abc) {
test(abc);
}
}
677 changes: 561 additions & 116 deletions src/lib.rs

Large diffs are not rendered by default.

25 changes: 25 additions & 0 deletions tests/locals.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#[cfg(feature = "loadjvm")]
pub mod test {
use jni_simple::*;

#[test]
fn test() {
unsafe {
load_jvm_from_java_home().expect("failed to load jvm");

let args: Vec<String> = vec![];

let (vm, env) = JNI_CreateJavaVM_with_string_args(JNI_VERSION_1_8, &args)
.expect("failed to create jvm");

let clazz = env.FindClass_str("java/lang/Object");
assert_eq!(JNI_OK, env.EnsureLocalCapacity(128));
assert_eq!(JNI_OK, env.PushLocalFrame(128));
let obj = env.AllocObject(clazz);
let n = env.NewGlobalRef(obj);
let r = env.PopLocalFrame(obj);
assert!(env.IsSameObject(r, n));
vm.DestroyJavaVM();
}
}
}
108 changes: 108 additions & 0 deletions tests/register_natives.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#[cfg(feature = "loadjvm")]
pub mod test {
use jni_simple::*;
use std::ffi::{c_void, CString};
use std::ptr::null_mut;

unsafe extern "system" fn t1(env: JNIEnv, _: jclass, param: jobject) {
assert!(!param.is_null());
let data = env.GetStringUTFChars_as_string(param).unwrap();
assert_eq!(data.as_str(), "test_string");
}

unsafe extern "system" fn t2(_env: JNIEnv, _: jclass, param: jdouble) {
assert_eq!(param, 754.156f64);
}

#[test]
fn test() {
unsafe {
load_jvm_from_java_home().expect("failed to load jvm");
let args: Vec<String> = vec![];
let (vm, env) = JNI_CreateJavaVM_with_string_args(JNI_VERSION_1_8, &args).expect("failed to create java VM");

let class_blob = include_bytes!("../java_testcode/RegisterTest.class");

let registered_class = env.DefineClass_str("RegisterTest", null_mut(), class_blob.as_slice());
let t1m = env.GetStaticMethodID_str(registered_class, "callTest", "(Ljava/lang/String;)V");
let t2m = env.GetStaticMethodID_str(registered_class, "callTest", "(D)V");
let test_string = env.NewStringUTF_str("test_string");

env.CallStaticVoidMethod1(registered_class, t1m, test_string);
assert!(env.ExceptionCheck());
let exc = env.ExceptionOccurred();
assert!(!exc.is_null());
env.ExceptionClear();
let exc_class = env.GetObjectClass(exc);
env.DeleteLocalRef(exc);
let class_class = env.GetObjectClass(exc_class);
let get_name_method = env.GetMethodID_str(class_class, "getName", "()Ljava/lang/String;");
env.DeleteLocalRef(class_class);
let exc_class_name = env.CallObjectMethod0(exc_class, get_name_method);
env.DeleteLocalRef(exc_class);
let exc_class_name_str = env.GetStringUTFChars_as_string(exc_class_name).unwrap();
env.DeleteLocalRef(exc_class_name);
assert_eq!(exc_class_name_str.as_str(), "java.lang.UnsatisfiedLinkError");

env.CallStaticVoidMethod1(registered_class, t2m, 754.156f64);
assert!(env.ExceptionCheck());
let exc = env.ExceptionOccurred();
assert!(!exc.is_null());
env.ExceptionClear();
let exc_class = env.GetObjectClass(exc);
env.DeleteLocalRef(exc);
let exc_class_name = env.CallObjectMethod0(exc_class, get_name_method);
env.DeleteLocalRef(exc_class);
let exc_class_name_str = env.GetStringUTFChars_as_string(exc_class_name).unwrap();
assert_eq!(exc_class_name_str.as_str(), "java.lang.UnsatisfiedLinkError");
env.DeleteLocalRef(exc_class_name);



let name = CString::new("test").unwrap();
let sig1 = CString::new("(Ljava/lang/String;)V").unwrap();
let sig2 = CString::new("(D)V").unwrap();

let method1 = JNINativeMethod::new(name.as_ptr(), sig1.as_ptr(), t1 as *const c_void);
let method2 = JNINativeMethod::new(name.as_ptr(), sig2.as_ptr(), t2 as *const c_void);

assert_eq!(JNI_OK, env.RegisterNatives_slice(registered_class, &[method1, method2]));

env.CallStaticVoidMethod1(registered_class, t2m, 754.156f64);
assert!(!env.ExceptionCheck());

env.CallStaticVoidMethod1(registered_class, t1m, test_string);
assert!(!env.ExceptionCheck());

env.UnregisterNatives(registered_class);

env.CallStaticVoidMethod1(registered_class, t2m, 754.156f64);
assert!(env.ExceptionCheck());
let exc = env.ExceptionOccurred();
assert!(!exc.is_null());
env.ExceptionClear();
let exc_class = env.GetObjectClass(exc);
env.DeleteLocalRef(exc);
let exc_class_name = env.CallObjectMethod0(exc_class, get_name_method);
env.DeleteLocalRef(exc_class);
let exc_class_name_str = env.GetStringUTFChars_as_string(exc_class_name).unwrap();
assert_eq!(exc_class_name_str.as_str(), "java.lang.UnsatisfiedLinkError");
env.DeleteLocalRef(exc_class_name);

env.CallStaticVoidMethod1(registered_class, t1m, test_string);
assert!(env.ExceptionCheck());
let exc = env.ExceptionOccurred();
assert!(!exc.is_null());
env.ExceptionClear();
let exc_class = env.GetObjectClass(exc);
env.DeleteLocalRef(exc);
let exc_class_name = env.CallObjectMethod0(exc_class, get_name_method);
env.DeleteLocalRef(exc_class);
let exc_class_name_str = env.GetStringUTFChars_as_string(exc_class_name).unwrap();
assert_eq!(exc_class_name_str.as_str(), "java.lang.UnsatisfiedLinkError");
env.DeleteLocalRef(exc_class_name);

vm.DestroyJavaVM();
}
}
}

0 comments on commit 22c8a9a

Please sign in to comment.