Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(bridge): allow proxies to be used after they have been garbage collected #60

Merged
merged 12 commits into from
Mar 3, 2023
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "java-bridge",
"version": "2.2.3-beta.2",
"version": "2.2.3-beta.3",
"main": "dist/index.prod.min.js",
"types": "dist/ts-src/index.d.ts",
"description": "A bridge between Node.js and Java APIs",
Expand Down
25 changes: 25 additions & 0 deletions src/node/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use std::sync::{Mutex, MutexGuard};

use lazy_static::lazy_static;

lazy_static! {
static ref CONFIG: Mutex<Config> = Mutex::new(Config::default());
}

pub struct Config {
pub run_event_loop_when_interface_proxy_is_active: bool,
}

impl Default for Config {
fn default() -> Self {
Config {
run_event_loop_when_interface_proxy_is_active: false,
}
}
}

impl Config {
pub fn get<'a>() -> MutexGuard<'a, Config> {
CONFIG.lock().unwrap()
}
}
26 changes: 20 additions & 6 deletions src/node/extensions/java_type_ext.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::node::helpers::js_to_java_object::{JsIntoJavaObject, JsToJavaClass};
use crate::node::helpers::napi_error::MapToNapiError;
use crate::node::helpers::napi_error::{MapToNapiError, NapiError};
use crate::node::interface_proxy::java_interface_proxy::JavaInterfaceProxy;
use crate::node::java_class_instance::OBJECT_PROPERTY;
use crate::node::java_interface_proxy::JavaInterfaceProxy;
use java_rs::java_call_result::JavaCallResult;
use java_rs::java_env::JavaEnv;
use java_rs::java_type::{JavaType, Type};
Expand Down Expand Up @@ -80,8 +80,14 @@ impl JsTypeEq for JavaType {
} else if other.is_buffer()? {
self.is_byte_array()
} else if JavaInterfaceProxy::instance_of(env.clone(), &other)? {
let proxy: JsObject = other.coerce_to_object()?.get_named_property("proxy")?;
let obj = env.unwrap::<GlobalJavaObject>(&proxy)?;
let proxy: JsUnknown = other.coerce_to_object()?.get_named_property("proxy")?;
if proxy.get_type()? == ValueType::Null {
return Err(
NapiError::from("The proxy has already been destroyed").into_napi()
);
}

let obj = env.unwrap::<GlobalJavaObject>(&proxy.coerce_to_object()?)?;
let j_env = obj.get_vm().attach_thread().map_napi_err()?;
let other_class = obj.get_class(&j_env).map_napi_err()?;

Expand Down Expand Up @@ -283,8 +289,16 @@ impl NapiToJava for JavaType {

let obj = value.coerce_to_object().map_err(err_fn)?;
if JavaInterfaceProxy::instance_of(node_env.clone(), &obj)? {
let proxy: JsObject = obj.get_named_property("proxy")?;
JavaObject::from(node_env.unwrap::<GlobalJavaObject>(&proxy)?.clone())
let proxy: JsUnknown = obj.get_named_property("proxy")?;
if proxy.get_type()? == ValueType::Null {
return Err("The proxy has already been destroyed".into());
}

JavaObject::from(
node_env
.unwrap::<GlobalJavaObject>(&proxy.coerce_to_object()?)?
.clone(),
)
} else {
let js_obj: JsObject =
obj.get_named_property(OBJECT_PROPERTY).map_err(err_fn)?;
Expand Down
58 changes: 58 additions & 0 deletions src/node/interface_proxy/function_caller.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
use crate::node::helpers::napi_error::MapToNapiError;
use java_rs::objects::java_object::JavaObject;
use java_rs::objects::object::GlobalJavaObject;

pub struct FunctionCaller {
instance: Option<GlobalJavaObject>,
}

impl FunctionCaller {
pub fn new(instance: GlobalJavaObject) -> Self {
Self {
instance: Some(instance),
}
}

pub fn is_alive(&self) -> bool {
self.instance.is_some()
}

pub fn is_dead(&self) -> bool {
self.instance.is_none()
}

pub fn destroy(&mut self) -> napi::Result<()> {
if let Some(instance) = self.instance.as_ref() {
let env = instance.get_vm().attach_thread().map_napi_err()?;
let java_class = env
.get_object_class(JavaObject::from(instance))
.map_napi_err()?;
let destruct = java_class
.get_void_method("destruct", "()V")
.map_napi_err()?;
destruct
.call(JavaObject::from(instance), &[])
.map_napi_err()?;

self.instance.take();
}

Ok(())
}

pub fn move_to(&mut self) -> Option<FunctionCaller> {
if let Some(obj) = self.instance.take() {
Some(FunctionCaller::new(obj))
} else {
None
}
}
}

impl Drop for FunctionCaller {
fn drop(&mut self) {
if let Err(e) = self.destroy() {
eprintln!("Error while dropping FunctionCaller: {}", e);
}
}
}
29 changes: 29 additions & 0 deletions src/node/interface_proxy/interface_call.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use crate::node::interface_proxy::types::JsCallResult;
use futures::channel::oneshot::Sender;
use java_rs::java_call_result::JavaCallResult;
use java_rs::util::util::ResultType;
use std::sync::Mutex;

pub struct InterfaceCall {
pub args: Vec<JavaCallResult>,
sender: Mutex<Option<Sender<JsCallResult>>>,
}

impl InterfaceCall {
pub fn new(args: Vec<JavaCallResult>, sender: Sender<JsCallResult>) -> Self {
InterfaceCall {
args,
sender: Mutex::new(Some(sender)),
}
}

pub fn set_result(&self, result: JsCallResult) -> ResultType<()> {
self.sender
.lock()
.unwrap()
.take()
.ok_or("The sender was already invoked".to_string())?
.send(result)
.map_err(|_| "Could not send result to sender".into())
}
}
15 changes: 15 additions & 0 deletions src/node/interface_proxy/interface_proxy_options.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/// Options for the interface proxies
#[napi(object)]
pub struct InterfaceProxyOptions {
/// If true, the proxy will be kept as a daemon
/// proxy after the interface has been destroyed
pub keep_as_daemon: Option<bool>,
}

impl Default for InterfaceProxyOptions {
fn default() -> Self {
Self {
keep_as_daemon: None,
}
}
}
Loading