1use std::ptr;
16
17use jni_sys::{jobject, jstring};
18
19use futures::channel::oneshot;
20
21use crate::errors::opt_to_res;
22use crate::{cache, errors, jni_utils, Instance, InvocationArg, Jvm};
23
24use super::logger::debug;
25
26impl Jvm {
27 pub async fn invoke_async(
30 &self,
31 instance: &Instance,
32 method_name: &str,
33 inv_args: &[InvocationArg],
34 ) -> errors::Result<Instance> {
35 debug(&format!(
36 "Asynchronously invoking method {} of class {} using {} arguments",
37 method_name,
38 instance.class_name,
39 inv_args.len()
40 ));
41 let (sender, rx) = oneshot::channel::<errors::Result<Instance>>();
43 unsafe {
44 Self::handle_channel_sender(self, sender, instance, method_name, inv_args)?;
45 }
46 let instance = rx.await?;
48 Self::do_return(self.jni_env, instance)?
49 }
50
51 pub async fn invoke_into_sendable_async(
59 instance: Instance,
60 method_name: String,
61 inv_args: Vec<InvocationArg>,
62 ) -> errors::Result<Instance> {
63 debug(&format!(
64 "Asynchronously invoking (2) method {} of class {} using {} arguments",
65 method_name,
66 instance.class_name,
67 inv_args.len()
68 ));
69 let (sender, rx) = oneshot::channel::<errors::Result<Instance>>();
71 unsafe {
72 let s = Jvm::attach_thread()?;
73 Self::handle_channel_sender(&s, sender, &instance, &method_name, inv_args.as_ref())?;
74 drop(s);
75 }
76
77 let instance = rx.await?;
79 let new_jvm = Jvm::attach_thread()?;
80 let new_jni_env = new_jvm.jni_env;
81 Self::do_return(new_jni_env, instance)?
82 }
83
84 unsafe fn handle_channel_sender(s: &Jvm, sender: oneshot::Sender<errors::Result<Instance>>, instance: &Instance, method_name: &str, inv_args: &[InvocationArg]) -> errors::Result<()> {
85 let tx = Box::new(sender);
86 let raw_ptr = Box::into_raw(tx);
88 let address_string = format!("{:p}", raw_ptr);
90 let address = i64::from_str_radix(&address_string[2..], 16).unwrap();
91
92 let method_name_jstring: jstring =
94 jni_utils::global_jobject_from_str(method_name, s.jni_env)?;
95
96 let size = inv_args.len() as i32;
98 let array_ptr = {
99 let j = (opt_to_res(cache::get_jni_new_object_array())?)(
100 s.jni_env,
101 size,
102 cache::get_invocation_arg_class()?,
103 ptr::null_mut(),
104 );
105 jni_utils::create_global_ref_from_local_ref(j, s.jni_env)?
106 };
107 let mut inv_arg_jobjects: Vec<jobject> = Vec::with_capacity(size as usize);
108
109 for i in 0..size {
111 let inv_arg_java =
113 inv_args[i as usize].as_java_ptr_with_global_ref(s.jni_env)?;
114 (opt_to_res(cache::get_jni_set_object_array_element())?)(
116 s.jni_env,
117 array_ptr,
118 i,
119 inv_arg_java,
120 );
121 inv_arg_jobjects.push(inv_arg_java);
122 }
123
124 (opt_to_res(cache::get_jni_call_void_method())?)(
126 s.jni_env,
127 instance.jinstance,
128 cache::get_invoke_async_method()?,
129 address,
130 method_name_jstring,
131 array_ptr,
132 );
133
134 Self::do_return(s.jni_env, ())?;
136
137 for inv_arg_jobject in inv_arg_jobjects {
139 jni_utils::delete_java_ref(s.jni_env, inv_arg_jobject);
140 }
141 jni_utils::delete_java_ref(s.jni_env, array_ptr);
142 jni_utils::delete_java_ref(s.jni_env, method_name_jstring);
143 Ok(())
144 }
145}
146
147#[cfg(test)]
148mod api_unit_tests {
149 use super::*;
150 use crate::lib_unit_tests::create_tests_jvm;
151 use futures::Future;
152 use tokio;
153
154 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
155 async fn invoke_async_success_w_tokio() -> errors::Result<()> {
156 let s_test = "j4rs_rust";
157 let jvm = create_tests_jvm()?;
158 let my_test = jvm.create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())?;
159 let instance = jvm
160 .invoke_async(
161 &my_test,
162 "getStringWithFuture",
163 &[InvocationArg::try_from(s_test)?],
164 )
165 .await?;
166 let string: String = jvm.to_rust(instance)?;
167 assert_eq!(s_test, string);
168 Ok(())
169 }
170
171 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
172 async fn invoke_async_failure_w_tokio() -> errors::Result<()> {
173 let s_test = "Boom!";
174 let jvm = create_tests_jvm()?;
175 let my_test = jvm.create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())?;
176 let instance_result = jvm
177 .invoke_async(
178 &my_test,
179 "getErrorWithFuture",
180 &[InvocationArg::try_from(s_test)?],
181 )
182 .await;
183 assert!(instance_result.is_err());
184 let error = instance_result.err().unwrap();
185 println!("{}", error);
186 Ok(())
187 }
188
189 #[async_std::test]
190 async fn invoke_async_success_w_async_std() -> errors::Result<()> {
191 let s_test = "j4rs_rust";
192 let jvm = create_tests_jvm()?;
193 let my_test = jvm.create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())?;
194 let instance = jvm
195 .invoke_async(
196 &my_test,
197 "getStringWithFuture",
198 &[InvocationArg::try_from(s_test)?],
199 )
200 .await?;
201 let string: String = jvm.to_rust(instance)?;
202 assert_eq!(s_test, string);
203 Ok(())
204 }
205
206 #[async_std::test]
207 async fn invoke_async_failure_w_async_std() -> errors::Result<()> {
208 let s_test = "Boom!";
209 let jvm = create_tests_jvm()?;
210 let my_test = jvm.create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())?;
211 let instance_result = jvm
212 .invoke_async(
213 &my_test,
214 "getErrorWithFuture",
215 &[InvocationArg::try_from(s_test)?],
216 )
217 .await;
218 assert!(instance_result.is_err());
219 let error = instance_result.err().unwrap();
220 println!("{}", error);
221 Ok(())
222 }
223
224 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
225 async fn invoke_async_and_reuse_instance() -> errors::Result<()> {
226 let s_test1 = "j4rs_rust1";
227 let s_test2 = "j4rs_rust2";
228 let jvm = create_tests_jvm()?;
229 let my_test = jvm.create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())?;
230 let instance1 = jvm
231 .invoke_async(
232 &my_test,
233 "getStringWithFuture",
234 &[InvocationArg::try_from(s_test1)?],
235 )
236 .await?;
237 let instance2 = jvm
238 .invoke_async(
239 &my_test,
240 "getStringWithFuture",
241 &[InvocationArg::try_from(s_test2)?],
242 )
243 .await?;
244 let string1: String = jvm.to_rust(instance1)?;
245 let string2: String = jvm.to_rust(instance2)?;
246 assert_eq!(s_test1, string1);
247 assert_eq!(s_test2, string2);
248 Ok(())
249 }
250
251 #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
252 async fn invoke_static_async() -> errors::Result<()> {
253 let s_test = "j4rs_rust";
254 let jvm = create_tests_jvm()?;
255 let my_test = jvm.static_class("org.astonbitecode.j4rs.tests.MyTest")?;
256 let instance = jvm
257 .invoke_async(
258 &my_test,
259 "getErrorWithFutureStatic",
260 &[InvocationArg::try_from(s_test)?],
261 )
262 .await?;
263 let string: String = jvm.to_rust(instance)?;
264 assert_eq!(s_test, string);
265 Ok(())
266 }
267
268 #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
269 async fn invoke_async_error_before_executing_async() -> errors::Result<()> {
270 let s_test = "j4rs_rust";
271 let jvm = create_tests_jvm()?;
272 let my_test = jvm.create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())?;
273 let instance_result = jvm
274 .invoke_async(&my_test, "echo", &[InvocationArg::try_from(s_test)?])
275 .await;
276 assert!(instance_result.is_err());
277 Ok(())
278 }
279
280 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
281 async fn invoke_void_future() -> errors::Result<()> {
282 let s_test = "j4rs_rust";
283 let jvm = create_tests_jvm()?;
284 let my_test = jvm.create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())?;
285 let instance_res = jvm
286 .invoke_async(
287 &my_test,
288 "executeVoidFuture",
289 &[InvocationArg::try_from(s_test)?],
290 )
291 .await;
292 assert!(instance_res.is_ok());
293 Ok(())
294 }
295
296 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
297 async fn invoke_into_sendable_async_success() -> errors::Result<()> {
298 let s_test = "j4rs_rust";
299 let jvm = create_tests_jvm()?;
300 let my_test = jvm.create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())?;
301 let instance = Jvm::invoke_into_sendable_async(
302 my_test,
303 "getStringWithFuture".to_string(),
304 vec![InvocationArg::try_from(s_test)?],
305 )
306 .await?;
307 let string: String = jvm.to_rust(instance)?;
308 assert_eq!(s_test, string);
309 Ok(())
310 }
311
312 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
313 async fn future_is_send() -> errors::Result<()> {
314 let s_test = "j4rs_rust";
315 let jvm = create_tests_jvm()?;
316 let my_test = jvm.create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())?;
317 let f = Jvm::invoke_into_sendable_async(
318 my_test,
319 "executeVoidFuture".to_string(),
320 vec![InvocationArg::try_from(s_test)?],
321 );
322 check_send(f);
323 Ok(())
324 }
325
326 fn check_send<F:Future>(_:F) where F:Send + 'static {}
327
328 async fn _memory_leaks_invoke_async_instances() -> errors::Result<()> {
330 let jvm = create_tests_jvm()?;
331 let instance = jvm.create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty() as &[InvocationArg; 0])?;
332 for i in 0..100000000 {
333 if i % 100000 == 0 {
334 println!("{}", i);
335 }
336 let ia = InvocationArg::try_from(i.to_string())?;
337 let _s = jvm
338 .invoke_async(&instance, "getStringWithFuture", &[ia])
339 .await?;
340 }
341 Ok(())
342 }
343}