use crate::{AssignableTo, Env, Global, JavaDebug, JavaDisplay, Local, ReferenceType};
use jni_sys::jobject;
use std::{
fmt::{self, Debug, Display, Formatter},
marker::PhantomData,
mem::transmute,
ops::Deref,
};
#[repr(C)] pub struct Ref<'env, T: ReferenceType> {
object: jobject,
env: Env<'env>,
_class: PhantomData<T>,
}
impl<'env, T: ReferenceType> std::ops::Receiver for Ref<'env, T> {
type Target = T;
}
impl<'env, T: ReferenceType> Ref<'env, T> {
pub unsafe fn from_raw(env: Env<'env>, object: jobject) -> Self {
Self {
object,
env,
_class: PhantomData,
}
}
pub fn env(&self) -> Env<'env> {
self.env
}
pub fn as_raw(&self) -> jobject {
self.object
}
pub fn as_global(&self) -> Global<T> {
let env = self.env();
let jnienv = env.as_raw();
let object = unsafe { ((**jnienv).v1_2.NewGlobalRef)(jnienv, self.as_raw()) };
assert!(!object.is_null());
unsafe { Global::from_raw(env.vm(), object) }
}
pub fn as_local(&self) -> Local<'env, T> {
let env = self.env();
let jnienv = env.as_raw();
let object = unsafe { ((**jnienv).v1_2.NewLocalRef)(jnienv, self.as_raw()) };
assert!(!object.is_null());
unsafe { Local::from_raw(self.env(), object) }
}
pub fn as_monitor(&'env self) -> Monitor<'env, T> {
Monitor::new(self)
}
pub fn is_same_object<O: ReferenceType>(&self, other: &Ref<'_, O>) -> bool {
let jnienv = self.env.as_raw();
unsafe { ((**jnienv).v1_2.IsSameObject)(jnienv, self.as_raw(), other.as_raw()) }
}
pub(crate) fn check_assignable<U: ReferenceType>(&self) -> Result<(), crate::CastError> {
let env = self.env();
let jnienv = env.as_raw();
let class = U::static_with_jni_type(|t| unsafe { env.require_class(t) });
let assignable = unsafe { ((**jnienv).v1_2.IsInstanceOf)(jnienv, self.as_raw(), class) };
unsafe {
((**jnienv).v1_2.DeleteLocalRef)(jnienv, class);
}
if assignable {
Ok(())
} else {
Err(crate::CastError)
}
}
pub unsafe fn cast_unchecked<U: ReferenceType>(self) -> Ref<'env, U> {
unsafe { transmute(self) }
}
pub fn cast<U: ReferenceType>(self) -> Result<Ref<'env, U>, crate::CastError> {
self.check_assignable::<U>()?;
Ok(unsafe { self.cast_unchecked() })
}
pub fn upcast<U: ReferenceType>(self) -> Ref<'env, U>
where
T: AssignableTo<U>,
{
unsafe { self.cast_unchecked() }
}
pub unsafe fn cast_ref_unchecked<U: ReferenceType>(&self) -> &Ref<'env, U> {
unsafe { transmute(self) }
}
pub fn cast_ref<U: ReferenceType>(&self) -> Result<&Ref<'env, U>, crate::CastError> {
self.check_assignable::<U>()?;
Ok(unsafe { self.cast_ref_unchecked() })
}
pub fn upcast_ref<U: ReferenceType>(&self) -> &Ref<'env, U>
where
T: AssignableTo<U>,
{
unsafe { self.cast_ref_unchecked() }
}
}
impl<'env, T: JavaDebug> Debug for Ref<'env, T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
T::fmt(self, f)
}
}
impl<'env, T: JavaDisplay> Display for Ref<'env, T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
T::fmt(self, f)
}
}
pub struct Monitor<'env, T: ReferenceType> {
inner: &'env Ref<'env, T>,
}
impl<'env, T: ReferenceType> Monitor<'env, T> {
fn new(reference: &'env Ref<'env, T>) -> Self {
let jnienv = reference.env.as_raw();
let result = unsafe { ((**jnienv).v1_2.MonitorEnter)(jnienv, reference.as_raw()) };
assert!(result == jni_sys::JNI_OK);
Self { inner: reference }
}
pub fn unlock(self) -> &'env Ref<'env, T> {
let inner = self.inner;
drop(self); inner
}
}
impl<'env, T: ReferenceType> Deref for Monitor<'env, T> {
type Target = Ref<'env, T>;
fn deref(&self) -> &Self::Target {
self.inner
}
}
impl<'env, T: ReferenceType> Drop for Monitor<'env, T> {
fn drop(&mut self) {
let env = self.inner.env;
let jnienv = env.as_raw();
let result = unsafe { ((**jnienv).v1_2.MonitorExit)(jnienv, self.inner.as_raw()) };
assert!(result == jni_sys::JNI_OK);
let exception = unsafe { ((**jnienv).v1_2.ExceptionOccurred)(jnienv) };
assert!(
exception.is_null(),
"exception happened calling JNI MonitorExit, the monitor is probably broken previously"
);
}
}