1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
use super::*;



/// A [Global](https://www.ibm.com/support/knowledgecenter/en/SSYKE2_8.0.0/com.ibm.java.vm.80.doc/docs/jni_refs.html),
/// non-null, reference to a Java object (+ &[VM]).
/// 
/// Unlike Local, this can be stored statically and shared between threads.  This has a few caveats:
/// * You must create a [GlobalRef] before use.
/// * The [Global] can be invalidated if the [VM] is unloaded.
/// 
/// **Not FFI Safe:**  #\[repr(rust)\], and exact layout is likely to change - depending on exact features used - in the
/// future.  Specifically, on Android, since we're guaranteed to only have a single ambient [VM], we can likely store the
/// *const JavaVM in static and/or thread local storage instead of lugging it around in every [Local].  Of course, there's
/// no guarantee that's actually an *optimization*...
/// 
/// [VM]:           struct.VM.html
/// [Global]:       struct.Global.html
/// [GlobalRef]:    type.GlobalRef.html
pub struct Global<Class: AsValidJObjectAndEnv> {
    pub(crate) global:  jobject,
    pub(crate) gen_vm:  GenVM,
    pub(crate) pd:      PhantomData<Class>,
}

unsafe impl<Class: AsValidJObjectAndEnv> Send for Global<Class> {}
unsafe impl<Class: AsValidJObjectAndEnv> Sync for Global<Class> {}

impl<Class: AsValidJObjectAndEnv> Global<Class> {
    pub fn with<'env>(&'env self, env: &'env Env) -> GlobalRef<'env, Class> {
        assert_eq!(self.gen_vm, env.get_gen_vm()); // Soundness check - env *must* belong to the same VM!
        unsafe { self.with_unchecked(env) }
    }

    pub unsafe fn with_unchecked<'env>(&'env self, env: &'env Env) -> GlobalRef<'env, Class> {
        let env = env.as_jni_env();
        GlobalRef {
            oae: ObjectAndEnv {
                object: self.global,
                env,
            },
            _env:   PhantomData,
            _class: PhantomData,
        }
    }
}

impl<'env, Class: AsValidJObjectAndEnv> From<Local<'env, Class>> for Global<Class> {
    fn from(local: Local<'env, Class>) -> Global<Class> {
        let env = unsafe { Env::from_ptr(local.oae.env) };
        let jnienv = env.as_jni_env();
        let gen_vm = env.get_gen_vm();
        let global = unsafe { (**jnienv).NewGlobalRef.unwrap()(jnienv, local.oae.object) };
        Global {
            global,
            gen_vm,
            pd: PhantomData,
        }
    }
}

impl<Class: AsValidJObjectAndEnv> Drop for Global<Class> {
    fn drop(&mut self) {
        VMS.read().unwrap().use_vm(self.gen_vm, |vm|{
            vm.with_env(|env|{
                let env = env.as_jni_env();
                unsafe { (**env).DeleteGlobalRef.unwrap()(env, self.global); }
            });
        });
    }
}



/// A [Global](https://www.ibm.com/support/knowledgecenter/en/SSYKE2_8.0.0/com.ibm.java.vm.80.doc/docs/jni_refs.html),
/// non-null, reference to a Java object (+ &Env).
/// 
/// Much like Local, the inclusion of an Env means this cannot be stored statically or shared between threads.
/// 
/// **Not FFI Safe:**  #\[repr(rust)\], and exact layout is likely to change - depending on exact features used - in the
/// future.  Specifically, on Android, since we're guaranteed to only have a single ambient VM, we can likely store the
/// \*const JNIEnv in thread local storage instead of lugging it around in every Local.  Of course, there's no
/// guarantee that's actually an *optimization*...
pub type GlobalRef<'env, Class> = Ref<'env, Class>;