use crate::logger::debug;
use crate::{cache, errors, jni_utils, InvocationArg, Jvm};
use jni_sys::jobject;
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::any::Any;
use std::convert::TryFrom;
use std::sync::mpsc::{Receiver, Sender};
#[derive(Serialize)]
pub struct Instance {
pub(crate) class_name: String,
#[serde(skip)]
pub(crate) jinstance: jobject,
#[serde(skip)]
pub(crate) skip_deleting_jobject: bool,
}
impl Instance {
pub(crate) fn new(obj: jobject, classname: &str) -> errors::Result<Instance> {
Ok(Instance {
jinstance: obj,
class_name: classname.to_string(),
skip_deleting_jobject: false,
})
}
pub fn class_name(&self) -> &str {
self.class_name.as_ref()
}
pub fn java_object(mut self) -> jobject {
self.skip_deleting_jobject = true;
self.jinstance
}
pub fn from_jobject(obj: jobject) -> errors::Result<Instance> {
let _jvm = cache::get_thread_local_env().map_err(|_| Jvm::attach_thread());
Ok(Instance {
jinstance: obj,
class_name: cache::UNKNOWN_FOR_RUST.to_string(),
skip_deleting_jobject: false,
})
}
pub fn from_jobject_with_global_ref(obj: jobject) -> errors::Result<Instance> {
let _jvm = cache::get_thread_local_env().map_err(|_| Jvm::attach_thread());
let global =
jni_utils::create_global_ref_from_local_ref(obj, cache::get_thread_local_env()?)?;
Ok(Instance {
jinstance: global,
class_name: cache::UNKNOWN_FOR_RUST.to_string(),
skip_deleting_jobject: false,
})
}
fn _weak_ref(&self) -> errors::Result<Instance> {
Ok(Instance {
class_name: self.class_name.clone(),
jinstance: jni_utils::_create_weak_global_ref_from_global_ref(
self.jinstance,
cache::get_thread_local_env()?,
)?,
skip_deleting_jobject: false,
})
}
}
impl TryFrom<InvocationArg> for Instance {
type Error = errors::J4RsError;
fn try_from(invocation_arg: InvocationArg) -> errors::Result<Instance> {
let obj = invocation_arg.as_java_ptr_with_local_ref(cache::get_thread_local_env()?)?;
Instance::new(obj, invocation_arg.class_name())
}
}
impl TryFrom<jobject> for Instance {
type Error = errors::J4RsError;
fn try_from(obj: jobject) -> errors::Result<Instance> {
Instance::from_jobject_with_global_ref(obj)
}
}
impl Drop for Instance {
fn drop(&mut self) {
debug(&format!("Dropping an instance of {}", self.class_name));
if !self.skip_deleting_jobject {
if let Some(j_env) = cache::get_thread_local_env_opt() {
jni_utils::delete_java_ref(j_env, self.jinstance);
}
}
}
}
unsafe impl Send for Instance {}
pub struct InstanceReceiver {
pub(crate) rx: Box<Receiver<Instance>>,
tx_address: u64,
}
impl InstanceReceiver {
pub(crate) fn new(rx: Receiver<Instance>, tx_address: u64) -> InstanceReceiver {
InstanceReceiver {
rx: Box::new(rx),
tx_address,
}
}
pub fn rx(&self) -> &Receiver<Instance> {
&self.rx
}
}
impl Drop for InstanceReceiver {
fn drop(&mut self) {
if self.tx_address > 0 {
debug("Dropping an InstanceReceiver");
let p = self.tx_address as *mut Sender<Instance>;
unsafe {
let tx = Box::from_raw(p);
drop(tx);
}
}
}
}
pub struct ChainableInstance<'a> {
instance: Instance,
jvm: &'a Jvm,
}
impl<'a> ChainableInstance<'a> {
pub(crate) fn new(instance: Instance, jvm: &'a Jvm) -> ChainableInstance<'a> {
ChainableInstance { instance, jvm }
}
pub(crate) fn new_with_instance_ref(
instance: &Instance,
jvm: &'a Jvm,
) -> errors::Result<ChainableInstance<'a>> {
let cloned = jvm.clone_instance(instance)?;
Ok(ChainableInstance {
instance: cloned,
jvm,
})
}
pub fn collect(self) -> Instance {
self.instance
}
pub fn invoke(
&self,
method_name: &str,
inv_args: &[InvocationArg],
) -> errors::Result<ChainableInstance<'_>> {
let instance = self.jvm.invoke(&self.instance, method_name, inv_args)?;
Ok(ChainableInstance::new(instance, self.jvm))
}
pub fn clone_instance(&self) -> errors::Result<ChainableInstance<'_>> {
let instance = self.jvm.clone_instance(&self.instance)?;
Ok(ChainableInstance::new(instance, self.jvm))
}
pub fn cast(&self, to_class: &str) -> errors::Result<ChainableInstance<'_>> {
let instance = self.jvm.cast(&self.instance, to_class)?;
Ok(ChainableInstance::new(instance, self.jvm))
}
pub fn field(&self, field_name: &str) -> errors::Result<ChainableInstance<'_>> {
let instance = self.jvm.field(&self.instance, field_name)?;
Ok(ChainableInstance::new(instance, self.jvm))
}
pub fn to_rust<T: Any>(self) -> errors::Result<T>
where
T: DeserializeOwned,
{
self.jvm.to_rust(self.instance)
}
pub fn to_rust_boxed<T: Any>(self) -> errors::Result<Box<T>>
where
T: DeserializeOwned,
{
self.jvm.to_rust_boxed(self.instance)
}
}
#[cfg(test)]
mod instance_unit_tests {
use crate::*;
use crate::lib_unit_tests::create_tests_jvm;
#[test]
fn is_null() -> errors::Result<()> {
let jvm = create_tests_jvm()?;
let test_instance = jvm
.create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())
?;
let maybe_null = jvm.invoke(&test_instance, "getNullInteger", InvocationArg::empty())?;
let is_null =
jvm.invoke_static(
"java.util.Objects",
"isNull",
&[InvocationArg::try_from(maybe_null)?])?;
let is_null: bool = jvm.to_rust(is_null)?;
assert!(is_null);
Ok(())
}
#[test]
fn try_from_jobject() -> errors::Result<()> {
let c = std::ptr::null_mut();
let instance = Instance::try_from(c)?;
assert!(instance.java_object() == std::ptr::null_mut());
Ok(())
}
}