use jni::errors::Error as JniError;
use jni::objects::{AutoLocal, GlobalRef, JObject};
use jni::sys::{jobject, jsize};
use jni::{AttachGuard, JNIEnv, JavaVM};
use std::os::raw::c_void;
pub type JniResult<T> = Result<T, JniError>;
pub enum EnvGuard<'a> {
Auto(JNIEnv<'a>),
Manual(AttachGuard<'a>),
}
impl<'a> EnvGuard<'a> {
pub fn new(vm: Option<&'a JavaVM>) -> JniResult<Self> {
let vm = vm.ok_or_else(|| JniError::from("no JVM reference found"))?;
Ok(match vm.get_env() {
Ok(env) => EnvGuard::Auto(env),
Err(_) => EnvGuard::Manual(vm.attach_current_thread()?),
})
}
pub fn env(&self) -> &JNIEnv {
match self {
EnvGuard::Auto(env) => &env,
EnvGuard::Manual(guard) => &*guard,
}
}
}
#[macro_export]
macro_rules! jni_unwrap {
($res:expr) => {{
let res: Result<_, JniError> = $res;
match res {
Ok(val) => val,
Err(e) => {
log::error!("{:?}", e);
return;
}
}
}};
}
#[macro_export]
macro_rules! gen_ctx {
($env:ident, $cb:ident) => {
{
let ctx = $crate::jni_unwrap!($env.new_global_ref($cb));
let ptr = *ctx.as_obj() as *mut c_void;
mem::forget(ctx);
ptr
}
};
($env:ident, $cb0:ident, $($cb_rest:ident),+ ) => {
{
let ctx = [
Some($crate::jni_unwrap!($env.new_global_ref($cb0))),
$(
Some($crate::jni_unwrap!($env.new_global_ref($cb_rest))),
)+
];
let ctx = Box::into_raw(Box::new(ctx)) as *mut c_void;
ctx
}
}
}
#[macro_export]
macro_rules! gen_primitive_type_converter {
($native_type:ty, $java_type:ty) => {
impl FromJava<$java_type> for $native_type {
fn from_java(_env: &JNIEnv, input: $java_type) -> JniResult<Self> {
Ok(input as Self)
}
}
impl<'a> ToJava<'a, $java_type> for $native_type {
fn to_java(&self, _env: &JNIEnv) -> JniResult<$java_type> {
Ok(*self as $java_type)
}
}
};
}
#[macro_export]
macro_rules! gen_object_array_converter {
($class_loader:expr, $native_type:ident, $java_ty_name:expr) => {
impl<'a, 'b> ToJava<'a, JObject<'a>> for &'b [$native_type] {
fn to_java(&self, env: &'a JNIEnv) -> JniResult<JObject<'a>> {
unsafe {
object_array_to_java(
$class_loader,
$native_type::to_java,
self,
env,
$java_ty_name,
)
}
}
}
};
}
#[macro_export]
macro_rules! gen_byte_array_converter {
($arr_type:ty, $size:expr) => {
impl<'a> FromJava<JObject<'a>> for [$arr_type; $size] {
fn from_java(env: &JNIEnv, input: JObject) -> JniResult<Self> {
let input = input.into_inner() as jbyteArray;
let mut output = [0; $size];
let len = env.get_array_length(input)? as usize;
env.get_byte_array_region(input, 0, &mut output[0..cmp::min(len, $size)])?;
Ok(unsafe { mem::transmute(output) })
}
}
impl<'a> ToJava<'a, JObject<'a>> for [$arr_type; $size] {
fn to_java(&self, env: &'a JNIEnv) -> JniResult<JObject<'a>> {
let output = env.new_byte_array(self.len() as jsize)?;
env.set_byte_array_region(output, 0, unsafe {
slice::from_raw_parts(self.as_ptr() as *const i8, self.len())
})?;
Ok(JObject::from(output as jobject))
}
}
};
}
#[allow(clippy::missing_safety_doc)]
pub unsafe fn object_array_to_java<'a, T, U: Into<JObject<'a>> + 'a>(
class_loader: unsafe fn(&'a JNIEnv, &str) -> JniResult<AutoLocal<'a>>,
transform_fn: fn(&T, &'a JNIEnv) -> JniResult<U>,
list: &[T],
env: &'a JNIEnv,
class: &str,
) -> JniResult<JObject<'a>> {
let cls = class_loader(env, class)?;
let output = env.new_object_array(list.len() as jsize, &cls, JObject::null())?;
for (idx, entry) in list.iter().enumerate() {
let jentry = transform_fn(entry, env)?.into();
env.set_object_array_element(output, idx as i32, jentry)?;
env.delete_local_ref(jentry)?;
}
Ok(JObject::from(output))
}
#[allow(clippy::missing_safety_doc)]
pub unsafe fn convert_cb_from_java(env: &JNIEnv, ctx: *mut c_void) -> JniResult<GlobalRef> {
Ok(GlobalRef::from_raw(env.get_java_vm()?, ctx as jobject))
}