To learn Rust, I write a library wrapping JNI. I ran into many life problems and bravely fought with the compiler to solve them, but with this I just give up. Here is the problem.
Now JNI is the interface of a Java virtual machine, so each Java object must be bound to a virtual machine. To do this, I created a "Java pointer type VM" JavaVM, which basically is a wrapper for the pointer and an interface for creating JavaEnv objects, which are only wrappers for JNIEnv *, as well as providers of a more secure interface for most JNI methods.
To declare that the JavaEnv object is bound to a virtual machine, I did the following:
pub struct JavaEnv<'a> {
phantom: PhantomData<&'a JavaVM>,
...
}
Now, if I understand correctly, all JavaEnv objects will be bound to some JavaVM object in terms of lifetime and will not survive it, which is exactly what I want.
JavaEnv is an interface for manipulating Java objects (and some others). Now all types of JNI objects implement the attribute:
pub trait JObject<'a>: Drop {
fn get_env(&self) -> &'a JavaEnv<'a>;
...
}
and they all look like this:
pub struct JavaObject<'a> {
env: &'a JavaEnv<'a>,
...
}
pub struct JavaClass<'a> {
env: &'a JavaEnv<'a>,
...
}
Now, if I understand correctly, all JavaObjects are bound to some JavaEnv object with a lifetime, which, in turn, is bound to a JavaVM object.
Finally, Java is a language that uses reference semantics by default, so comparing objects is just a superficial comparison of links, and I wanted to reflect it in the Rust interface:
impl<'a, R: 'a + JObject<'a>> PartialEq<R> for JavaObject<'a> {
fn eq(&self, other: &R) -> bool {
self.get_env().is_same_object(self, other)
}
}
impl<'a, R: 'a + JObject<'a>> PartialEq<R> for JavaClass<'a> {
fn eq(&self, other: &R) -> bool {
self.get_env().is_same_object(self, other)
}
}
pub fn JavaEnv::is_same_object<T1: 'a + JObject<'a>, T2: 'a + JObject<'a>>(&self, obj1: &T1, obj2: &T2) -> bool {
unsafe {
((**self.ptr).IsSameObject)(self.ptr, obj1.get_obj(), obj2.get_obj()) == JNI_TRUE
}
}
This does not work. Here's the test:
let (cls, cap) = JavaClass::find(&env, "java/lang/String", cap).unwrap();
let (obj /*of class java/lang/String*/, cap) = cls.alloc(cap).unwrap();
let cls1 /*also java/lang/String*/ = obj.get_class(&cap);
assert!(cls1 == cls);
let (sobj /*also of java/lang/String*/, cap) = JavaString::new(&env, "hi!", cap).unwrap();
assert!(cls1 != sobj);
let scls /*also java/lang/String*/ = sobj.get_class(&cap);
assert!(scls == cls1);
assert!(scls == cls);
// TODO: somehow these cls1, scls and cls have different lifetimes (or not?)
// So those two asserts do not compile!!
assert!(cls1 == scls);
assert!(cls == scls);
To “fix”, I had to change the eq code:
impl<'a, 'b, R: 'b + JObject<'b>> PartialEq<R> for JavaObject<'a> {
fn eq(&self, other: &R) -> bool {
self.get_env().is_same_object(self, other)
}
}
impl<'a, 'b, R: 'b + JObject<'b>> PartialEq<R> for JavaClass<'a> {
fn eq(&self, other: &R) -> bool {
self.get_env().is_same_object(self, other)
}
}
pub fn JavaEnv::is_same_object<'b, T1: 'a + JObject<'a>, T2: 'b + JObject<'b>>(&self, obj1: &T1, obj2: &T2) -> bool {
unsafe {
((**self.ptr).IsSameObject)(self.ptr, obj1.get_obj(), obj2.get_obj()) == JNI_TRUE
}
}
JObjects, !
: , ? JavaEnv , , JavaVM!
, : , ?