Skip to content

Commit bda0114

Browse files
committed
Implement check_equals
1 parent 5ecf573 commit bda0114

File tree

6 files changed

+122
-0
lines changed

6 files changed

+122
-0
lines changed

java/src/main/java/org/astonbitecode/j4rs/api/Instance.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,4 +142,16 @@ default T getOrDeserializeJavaObject() {
142142
return (T) objValue.getObject();
143143
}
144144
}
145+
146+
default <U> boolean checkEquals(Instance<U> other) {
147+
T a = this.getOrDeserializeJavaObject();
148+
U b = other.getOrDeserializeJavaObject();
149+
if (a != null && b != null) {
150+
return a.equals(b);
151+
} else if (a == null && b == null) {
152+
return true;
153+
} else {
154+
return false;
155+
}
156+
}
145157
}

java/src/test/java/org/astonbitecode/j4rs/api/invocation/JsonInvocationImplTest.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import org.astonbitecode.j4rs.api.Instance;
1818
import org.astonbitecode.j4rs.api.dtos.InvocationArg;
1919
import org.astonbitecode.j4rs.api.instantiation.NativeInstantiationImpl;
20+
import org.astonbitecode.j4rs.api.value.NullObject;
2021
import org.astonbitecode.j4rs.errors.InvocationException;
2122
import org.astonbitecode.j4rs.tests.MyTestTest;
2223
import org.astonbitecode.j4rs.utils.*;
@@ -296,6 +297,19 @@ public void invokeOverloaded() throws ClassNotFoundException {
296297
instance.invoke("addInts", new InvocationArg(list.getClass().getName(), list));
297298
}
298299

300+
@Test
301+
public void checkEquals() {
302+
JsonInvocationImpl i1 = new JsonInvocationImpl(Integer.valueOf(3), Integer.class);
303+
JsonInvocationImpl i2 = new JsonInvocationImpl(Integer.valueOf(3), Integer.class);
304+
assert (i1.checkEquals(i2));
305+
JsonInvocationImpl i3 = new JsonInvocationImpl(Integer.valueOf(33), Integer.class);
306+
assert (!i1.checkEquals(i3));
307+
JsonInvocationImpl iNull1 = new JsonInvocationImpl(null, NullObject.class);
308+
assert (!iNull1.checkEquals(i3));
309+
JsonInvocationImpl iNull2 = new JsonInvocationImpl(null, NullObject.class);
310+
assert (iNull1.checkEquals(iNull2));
311+
}
312+
299313
private class TestCallback extends NativeCallbackToRustFutureSupport {
300314
private AtomicReference<String> s = new AtomicReference<>(null);
301315

200 Bytes
Binary file not shown.

rust/src/api/mod.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,9 @@ impl Jvm {
253253
let _ = cache::get_jni_call_object_method().or_else(|| {
254254
cache::set_jni_call_object_method(Some((**jni_environment).v1_6.CallObjectMethod))
255255
});
256+
let _ = cache::get_jni_call_boolean_method().or_else(|| {
257+
cache::set_jni_call_boolean_method(Some((**jni_environment).v1_6.CallBooleanMethod))
258+
});
256259
let _ = cache::get_jni_call_byte_method().or_else(|| {
257260
cache::set_jni_call_byte_method(Some((**jni_environment).v1_6.CallByteMethod))
258261
});
@@ -1205,6 +1208,31 @@ impl Jvm {
12051208
}
12061209
}
12071210

1211+
/// Checks whether an Instance a is equal to some InvocationArg.
1212+
///
1213+
/// The check is actually against the Java `Object.equals`, taking into consideration the possibility of null.
1214+
/// `NullPointerException` will not be thrown, even if one of the inputs is null.
1215+
pub fn check_equals(&self, instance: impl Borrow<Instance>, inv_arg: impl Borrow<InvocationArg>) -> errors::Result<bool> {
1216+
debug(&format!("Checking equality between instances of {} and {}", instance.borrow().class_name(), inv_arg.borrow().class_name()));
1217+
unsafe {
1218+
// Create InvocationArg Java Objects
1219+
let inv_arg_java_b = inv_arg.borrow().as_java_ptr_with_global_ref(self.jni_env)?;
1220+
// Call the checkEquals method
1221+
let java_boolean = (opt_to_res(cache::get_jni_call_boolean_method())?)(
1222+
self.jni_env,
1223+
instance.borrow().jinstance,
1224+
cache::get_check_equals_method()?,
1225+
inv_arg_java_b,
1226+
);
1227+
1228+
// Create and return the boolean
1229+
Self::do_return(
1230+
self.jni_env,
1231+
java_boolean,
1232+
)
1233+
}
1234+
}
1235+
12081236
/// Returns the Rust representation of the provided instance, boxed
12091237
pub fn to_rust_boxed<T>(&self, instance: Instance) -> errors::Result<Box<T>>
12101238
where

rust/src/cache.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ pub(crate) type JniCallIntMethod =
6868
#[allow(non_snake_case)]
6969
pub(crate) type JniCallByteMethod =
7070
unsafe extern "C" fn(_: *mut JNIEnv, _: jobject, _: jmethodID, ...) -> jbyte;
71+
#[allow(non_snake_case)]
72+
pub(crate) type JniCallBooleanMethod =
73+
unsafe extern "C" fn(_: *mut JNIEnv, _: jobject, _: jmethodID, ...) -> jboolean;
7174
#[allow(non_snake_case)]
7275
pub(crate) type JniCallShortMethod =
7376
unsafe extern "C" fn(_: *mut JNIEnv, _: jobject, _: jmethodID, ...) -> jshort;
@@ -233,6 +236,7 @@ thread_local! {
233236
pub(crate) static JNI_CALL_OBJECT_METHOD: RefCell<Option<JniCallObjectMethod>> = RefCell::new(None);
234237
pub(crate) static JNI_CALL_INT_METHOD: RefCell<Option<JniCallIntMethod>> = RefCell::new(None);
235238
pub(crate) static JNI_CALL_BYTE_METHOD: RefCell<Option<JniCallByteMethod>> = RefCell::new(None);
239+
pub(crate) static JNI_CALL_BOOLEAN_METHOD: RefCell<Option<JniCallBooleanMethod>> = RefCell::new(None);
236240
pub(crate) static JNI_CALL_SHORT_METHOD: RefCell<Option<JniCallShortMethod>> = RefCell::new(None);
237241
pub(crate) static JNI_CALL_CHAR_METHOD: RefCell<Option<JniCallCharMethod>> = RefCell::new(None);
238242
pub(crate) static JNI_CALL_LONG_METHOD: RefCell<Option<JniCallLongMethod>> = RefCell::new(None);
@@ -297,6 +301,8 @@ thread_local! {
297301
pub(crate) static CAST_STATIC_METHOD: RefCell<Option<jmethodID>> = RefCell::new(None);
298302
// The get json method
299303
pub(crate) static GET_JSON_METHOD: RefCell<Option<jmethodID>> = RefCell::new(None);
304+
// The get checkEquals method
305+
pub(crate) static CHECK_EQUALS_METHOD: RefCell<Option<jmethodID>> = RefCell::new(None);
300306
// The get object class name method
301307
pub(crate) static GET_OBJECT_CLASS_NAME_METHOD: RefCell<Option<jmethodID>> = RefCell::new(None);
302308
// The get object method
@@ -504,6 +510,18 @@ pub(crate) fn get_jni_call_byte_method() -> Option<JniCallByteMethod> {
504510
JNI_CALL_BYTE_METHOD.with(|opt| *opt.borrow())
505511
}
506512

513+
pub(crate) fn set_jni_call_boolean_method(j: Option<JniCallBooleanMethod>) -> Option<JniCallBooleanMethod> {
514+
debug("Called set_jni_call_boolean_method");
515+
JNI_CALL_BOOLEAN_METHOD.with(|opt| {
516+
*opt.borrow_mut() = j;
517+
});
518+
get_jni_call_boolean_method()
519+
}
520+
521+
pub(crate) fn get_jni_call_boolean_method() -> Option<JniCallBooleanMethod> {
522+
JNI_CALL_BOOLEAN_METHOD.with(|opt| *opt.borrow())
523+
}
524+
507525
pub(crate) fn set_jni_call_short_method(
508526
j: Option<JniCallShortMethod>,
509527
) -> Option<JniCallShortMethod> {
@@ -1416,6 +1434,41 @@ pub(crate) fn get_get_json_method() -> errors::Result<jmethodID> {
14161434
)
14171435
}
14181436

1437+
pub(crate) fn set_check_equals_method(j: jmethodID) {
1438+
debug("Called set_check_equals_method");
1439+
CHECK_EQUALS_METHOD.with(|opt| {
1440+
*opt.borrow_mut() = Some(j);
1441+
});
1442+
}
1443+
1444+
pub(crate) fn get_check_equals_method() -> errors::Result<jmethodID> {
1445+
get_cached!(
1446+
CHECK_EQUALS_METHOD,
1447+
{
1448+
let env = get_thread_local_env()?;
1449+
1450+
let check_equals_method_signature = format!("(L{};)Z", INVO_IFACE_NAME);
1451+
let cstr1 = utils::to_c_string("checkEquals");
1452+
let cstr2 = utils::to_c_string(check_equals_method_signature.as_ref());
1453+
1454+
// Get the method ID for the `Instance.checkEquals`
1455+
let j = unsafe {
1456+
(opt_to_res(get_jni_get_method_id())?)(
1457+
env,
1458+
get_java_instance_class()?,
1459+
cstr1,
1460+
cstr2,
1461+
)
1462+
};
1463+
utils::drop_c_string(cstr1);
1464+
utils::drop_c_string(cstr2);
1465+
1466+
j
1467+
},
1468+
set_check_equals_method
1469+
)
1470+
}
1471+
14191472
pub(crate) fn set_get_object_class_name_method(j: jmethodID) {
14201473
debug("Called set_get_object_class_name_method");
14211474
GET_OBJECT_CLASS_NAME_METHOD.with(|opt| {

rust/src/lib.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1287,4 +1287,19 @@ mod lib_unit_tests {
12871287

12881288
Ok(())
12891289
}
1290+
1291+
#[test]
1292+
fn check_equals() -> errors::Result<()> {
1293+
let jvm = create_tests_jvm()?;
1294+
let test_instance = jvm
1295+
.create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())
1296+
?;
1297+
let integer_instance = jvm.create_instance("java.lang.Integer", &[InvocationArg::try_from(3_i32)?.into_primitive()?])?;
1298+
let ia1 = InvocationArg::try_from(3)?;
1299+
assert!(jvm.check_equals(&integer_instance, &ia1)?);
1300+
1301+
let null_instance = jvm.invoke(&test_instance, "getNullInteger", InvocationArg::empty())?;
1302+
assert!(jvm.check_equals(&null_instance, InvocationArg::try_from(Null::Integer)?)?);
1303+
Ok(())
1304+
}
12901305
}

0 commit comments

Comments
 (0)