java_spaghetti/vm.rs
1use std::ptr::null_mut;
2
3use jni_sys::*;
4
5use crate::Env;
6
7/// FFI: Use **&VM** instead of *const JavaVM. This represents a global, process-wide Java exection environment.
8///
9/// On Android, there is only one VM per-process, although on desktop it's possible (if rare) to have multiple VMs
10/// within the same process. This library does not support having multiple VMs active simultaniously.
11///
12/// This is a "safe" alternative to jni_sys::JavaVM raw pointers, with the following caveats:
13///
14/// 1) A null vm will result in **undefined behavior**. Java should not be invoking your native functions with a null
15/// *mut JavaVM, however, so I don't believe this is a problem in practice unless you've bindgened the C header
16/// definitions elsewhere, calling them (requiring `unsafe`), and passing null pointers (generally UB for JNI
17/// functions anyways, so can be seen as a caller soundness issue.)
18///
19/// 2) Allowing the underlying JavaVM to be modified is **undefined behavior**. I don't believe the JNI libraries
20/// modify the JavaVM, so as long as you're not accepting a *mut JavaVM elsewhere, using unsafe to dereference it,
21/// and mucking with the methods on it yourself, I believe this "should" be fine.
22#[repr(transparent)]
23#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
24pub struct VM(*mut JavaVM);
25
26impl VM {
27 pub fn as_raw(&self) -> *mut JavaVM {
28 self.0
29 }
30
31 pub unsafe fn from_raw(vm: *mut JavaVM) -> Self {
32 Self(vm)
33 }
34
35 pub fn with_env<F, R>(&self, callback: F) -> R
36 where
37 F: for<'env> FnOnce(Env<'env>) -> R,
38 {
39 let mut env = null_mut();
40 match unsafe { ((**self.0).v1_2.GetEnv)(self.0, &mut env, JNI_VERSION_1_2) } {
41 JNI_OK => callback(unsafe { Env::from_raw(env as _) }),
42 JNI_EDETACHED => match unsafe { ((**self.0).v1_2.AttachCurrentThread)(self.0, &mut env, null_mut()) } {
43 JNI_OK => callback(unsafe { Env::from_raw(env as _) }),
44 unexpected => panic!("AttachCurrentThread returned unknown error: {}", unexpected),
45 },
46 JNI_EVERSION => panic!("GetEnv returned JNI_EVERSION"),
47 unexpected => panic!("GetEnv returned unknown error: {}", unexpected),
48 }
49 }
50}
51
52unsafe impl Send for VM {}
53unsafe impl Sync for VM {}