j4rs/
jni_utils.rs

1// Copyright 2019 astonbitecode
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::mem;
16use std::os::raw::{c_char, c_double};
17use std::ptr;
18
19use jni_sys::{jint, jobject, jobjectRefType, jstring, JNIEnv, JNI_TRUE};
20
21use crate::cache;
22use crate::errors;
23use crate::errors::opt_to_res;
24use crate::logger::{debug, error};
25use crate::utils;
26use crate::{InvocationArg, Jvm};
27
28pub(crate) fn invocation_arg_jobject_from_rust_serialized(
29    ia: &InvocationArg,
30    jni_env: *mut JNIEnv,
31    create_global: bool,
32) -> errors::Result<jobject> {
33    unsafe {
34        let (class_name, json) = match ia {
35            _s @ &InvocationArg::Java { .. } | _s @ &InvocationArg::RustBasic { .. } => {
36                panic!("Called invocation_arg_jobject_from_rust_serialized for an InvocationArg that contains an object. Please consider opening a bug to the developers.")
37            }
38            InvocationArg::Rust {
39                class_name,
40                json,
41                ..
42            } => {
43                debug(&format!(
44                    "Creating jobject from Rust with serialized representation for class {}",
45                    class_name
46                ));
47                (class_name.to_owned(), json.to_owned())
48            }
49        };
50
51        let class_name_jstring = global_jobject_from_str(&class_name, jni_env)?;
52        let json_jstring = global_jobject_from_str(&json, jni_env)?;
53
54        debug(&format!(
55            "Calling the InvocationArg constructor with '{}'",
56            class_name
57        ));
58        let inv_arg_instance = (opt_to_res(cache::get_jni_new_object())?)(
59            jni_env,
60            cache::get_invocation_arg_class()?,
61            cache::get_inv_arg_rust_constructor_method()?,
62            // First argument: class_name
63            class_name_jstring,
64            // Second argument: json
65            json_jstring,
66        );
67
68        // Check for exceptions
69        Jvm::do_return(jni_env, ())?;
70        delete_java_ref(jni_env, class_name_jstring);
71        delete_java_ref(jni_env, json_jstring);
72
73        if create_global {
74            Ok(create_global_ref_from_local_ref(inv_arg_instance, jni_env)?)
75        } else {
76            Ok(inv_arg_instance)
77        }
78    }
79}
80
81pub(crate) fn invocation_arg_jobject_from_rust_basic(
82    ia: &InvocationArg,
83    jni_env: *mut JNIEnv,
84    create_global: bool,
85) -> errors::Result<jobject> {
86    unsafe {
87        let (class_name, jinstance) = match ia {
88            _s @ &InvocationArg::Java { .. } => {
89                panic!("Called invocation_arg_jobject_from_rust_basic for an InvocationArg that contains an object from Java. Please consider opening a bug to the developers.")
90            }
91            _s @ &InvocationArg::Rust { .. } => {
92                panic!("Called invocation_arg_jobject_from_rust_basic for an InvocationArg that contains a serialized object. Please consider opening a bug to the developers.")
93            }
94            InvocationArg::RustBasic {
95                class_name,
96                instance,
97                ..
98            } => {
99                debug(&format!(
100                    "Creating jobject from Rust basic for class {}",
101                    class_name
102                ));
103                (class_name.to_owned(), instance.jinstance)
104            }
105        };
106        debug(&format!(
107            "Calling the InvocationArg constructor with '{}'",
108            class_name
109        ));
110        let class_name_jstring = global_jobject_from_str(&class_name, jni_env)?;
111
112        let inv_arg_instance = (opt_to_res(cache::get_jni_new_object())?)(
113            jni_env,
114            cache::get_invocation_arg_class()?,
115            cache::get_inv_arg_basic_rust_constructor_method()?,
116            // First argument: class_name
117            class_name_jstring,
118            // Second argument: Instance instance
119            jinstance,
120        );
121
122        delete_java_ref(jni_env, class_name_jstring);
123
124        if create_global {
125            Ok(create_global_ref_from_local_ref(inv_arg_instance, jni_env)?)
126        } else {
127            Ok(inv_arg_instance)
128        }
129    }
130}
131
132pub(crate) fn invocation_arg_jobject_from_java(
133    ia: &InvocationArg,
134    jni_env: *mut JNIEnv,
135    create_global: bool,
136) -> errors::Result<jobject> {
137    unsafe {
138        let (class_name, jinstance) = match ia {
139            _s @ &InvocationArg::Rust { .. } => panic!("Called invocation_arg_jobject_from_java for an InvocationArg that is created by Rust. Please consider opening a bug to the developers."),
140            &InvocationArg::Java { ref class_name, ref instance, .. } | &InvocationArg::RustBasic { ref class_name, ref instance, .. } => {
141                debug(&format!("Creating jobject from Java for class {}", class_name));
142                (class_name.to_owned(), instance.jinstance)
143            }
144        };
145
146        debug(&format!(
147            "Calling the InvocationArg constructor for class '{}'",
148            class_name
149        ));
150
151        let class_name_jstring = global_jobject_from_str(&class_name, jni_env)?;
152
153        let inv_arg_instance = (opt_to_res(cache::get_jni_new_object())?)(
154            jni_env,
155            cache::get_invocation_arg_class()?,
156            cache::get_inv_arg_java_constructor_method()?,
157            // First argument: class_name
158            class_name_jstring,
159            // Second argument: Instance instance
160            jinstance,
161        );
162
163        // Check for exceptions
164        Jvm::do_return(jni_env, ())?;
165        delete_java_ref(jni_env, class_name_jstring);
166
167        if create_global {
168            Ok(create_global_ref_from_local_ref(inv_arg_instance, jni_env)?)
169        } else {
170            Ok(inv_arg_instance)
171        }
172    }
173}
174
175pub fn create_global_ref_from_local_ref(
176    local_ref: jobject,
177    jni_env: *mut JNIEnv,
178) -> errors::Result<jobject> {
179    unsafe {
180        let ngr = (**jni_env).v1_6.NewGlobalRef;
181        let exc = (**jni_env).v1_6.ExceptionCheck;
182        let exd = (**jni_env).v1_6.ExceptionDescribe;
183        let exclear = (**jni_env).v1_6.ExceptionClear;
184        let gort = (**jni_env).v1_6.GetObjectRefType;
185        // Create the global ref
186        let global = ngr(
187            jni_env,
188            local_ref,
189        );
190        // If local ref, delete it
191        if gort(jni_env, local_ref) as jint == jobjectRefType::JNILocalRefType as jint {
192            delete_java_local_ref(jni_env, local_ref);
193        }
194        // Exception check
195        if (exc)(jni_env) == JNI_TRUE {
196            (exd)(jni_env);
197            (exclear)(jni_env);
198            Err(errors::J4RsError::JavaError("An Exception was thrown by Java while creating global ref... Please check the logs or the console.".to_string()))
199        } else {
200            Ok(global)
201        }
202    }
203}
204
205pub(crate) fn _create_weak_global_ref_from_global_ref(
206    global_ref: jobject,
207    jni_env: *mut JNIEnv,
208) -> errors::Result<jobject> {
209    unsafe {
210        let nwgr = (**jni_env).v1_6.NewWeakGlobalRef;
211        let exc = (**jni_env).v1_6.ExceptionCheck;
212        let exd = (**jni_env).v1_6.ExceptionDescribe;
213        let exclear = (**jni_env).v1_6.ExceptionClear;
214
215        // Create the weak global ref
216        let global = nwgr(jni_env, global_ref);
217        // Exception check
218        if (exc)(jni_env) == JNI_TRUE {
219            (exd)(jni_env);
220            (exclear)(jni_env);
221            Err(errors::J4RsError::JavaError("An Exception was thrown by Java while creating a weak global ref... Please check the logs or the console.".to_string()))
222        } else {
223            Ok(global)
224        }
225    }
226}
227
228/// Deletes the java ref from the memory
229pub fn delete_java_ref(jni_env: *mut JNIEnv, jinstance: jobject) {
230    unsafe {
231        let dgr = (**jni_env).v1_6.DeleteGlobalRef;
232        let exc = (**jni_env).v1_6.ExceptionCheck;
233        let exd = (**jni_env).v1_6.ExceptionDescribe;
234        let exclear = (**jni_env).v1_6.ExceptionClear;
235        dgr(jni_env, jinstance);
236        if (exc)(jni_env) == JNI_TRUE {
237            (exd)(jni_env);
238            (exclear)(jni_env);
239            error(
240                "An Exception was thrown by Java... Please check the logs or the console.",
241            );
242        }
243    }
244}
245
246/// Deletes the java ref from the memory
247pub(crate) fn delete_java_local_ref(jni_env: *mut JNIEnv, jinstance: jobject) {
248    unsafe {
249        let dlr = (**jni_env).v1_6.DeleteLocalRef;
250        let exc = (**jni_env).v1_6.ExceptionCheck;
251        let exd = (**jni_env).v1_6.ExceptionDescribe;
252        let exclear = (**jni_env).v1_6.ExceptionClear;
253        dlr(jni_env, jinstance);
254        if (exc)(jni_env) == JNI_TRUE {
255            (exd)(jni_env);
256            (exclear)(jni_env);
257            error(
258                "An Exception was thrown by Java... Please check the logs or the console.",
259            );
260        }
261    }
262}
263
264pub(crate) fn global_jobject_from_str(
265    string: &str,
266    jni_env: *mut JNIEnv,
267) -> errors::Result<jobject> {
268    let obj = local_jobject_from_str(string, jni_env)?;
269    let gr = create_global_ref_from_local_ref(obj, jni_env)?;
270    Ok(gr)
271}
272
273pub(crate) fn local_jobject_from_str(
274    string: &str,
275    jni_env: *mut JNIEnv,
276) -> errors::Result<jobject> {
277    unsafe {
278        let tmp = utils::to_c_string_struct(string);
279        Ok((opt_to_res(cache::get_jni_new_string_utf())?)(jni_env, tmp.as_ptr()))
280    }
281}
282
283pub(crate) fn global_jobject_from_i8(a: &i8, jni_env: *mut JNIEnv) -> errors::Result<jobject> {
284    unsafe {
285        let tmp = *a as *const i8;
286        let o = (opt_to_res(cache::get_jni_new_object())?)(
287            jni_env,
288            cache::get_byte_class()?,
289            cache::get_byte_constructor_method()?,
290            tmp as *const i8,
291        );
292        create_global_ref_from_local_ref(o, jni_env)
293    }
294}
295
296pub(crate) unsafe fn i8_from_jobject(obj: jobject, jni_env: *mut JNIEnv) -> errors::Result<i8> {
297    if obj.is_null() {
298        Err(errors::J4RsError::JniError(
299            "Attempt to create an i8 from null".to_string(),
300        ))
301    } else {
302        let v = (opt_to_res(cache::get_jni_call_byte_method())?)(
303            jni_env,
304            obj,
305            cache::get_byte_to_byte_method()?,
306        );
307        Ok(v as i8)
308    }
309}
310
311pub(crate) fn global_jobject_from_i16(a: &i16, jni_env: *mut JNIEnv) -> errors::Result<jobject> {
312    unsafe {
313        let tmp = *a as *const i16;
314        let o = (opt_to_res(cache::get_jni_new_object())?)(
315            jni_env,
316            cache::get_short_class()?,
317            cache::get_short_constructor_method()?,
318            tmp as *const i16,
319        );
320        create_global_ref_from_local_ref(o, jni_env)
321    }
322}
323
324pub(crate) fn global_jobject_from_u16(a: &u16, jni_env: *mut JNIEnv) -> errors::Result<jobject> {
325    unsafe {
326        let tmp = *a as *const u16;
327        let o = (opt_to_res(cache::get_jni_new_object())?)(
328            jni_env,
329            cache::get_character_class()?,
330            cache::get_character_constructor_method()?,
331            tmp as *const u16,
332        );
333        create_global_ref_from_local_ref(o, jni_env)
334    }
335}
336
337pub(crate) unsafe fn i16_from_jobject(obj: jobject, jni_env: *mut JNIEnv) -> errors::Result<i16> {
338    if obj.is_null() {
339        Err(errors::J4RsError::JniError(
340            "Attempt to create an i16 from null".to_string(),
341        ))
342    } else {
343        let v = (opt_to_res(cache::get_jni_call_short_method())?)(
344            jni_env,
345            obj,
346            cache::get_short_to_short_method()?,
347        );
348        Ok(v as i16)
349    }
350}
351
352pub(crate) unsafe fn u16_from_jobject(obj: jobject, jni_env: *mut JNIEnv) -> errors::Result<u16> {
353    if obj.is_null() {
354        Err(errors::J4RsError::JniError(
355            "Attempt to create an u16 from null".to_string(),
356        ))
357    } else {
358        let v = (opt_to_res(cache::get_jni_call_char_method())?)(
359            jni_env,
360            obj,
361            cache::get_character_to_char_method()?,
362        );
363        Ok(v as u16)
364    }
365}
366
367pub(crate) fn global_jobject_from_i32(a: &i32, jni_env: *mut JNIEnv) -> errors::Result<jobject> {
368    unsafe {
369        let tmp = *a as *const i32;
370        let o = (opt_to_res(cache::get_jni_new_object())?)(
371            jni_env,
372            cache::get_integer_class()?,
373            cache::get_integer_constructor_method()?,
374            tmp as *const i32,
375        );
376        create_global_ref_from_local_ref(o, jni_env)
377    }
378}
379
380pub(crate) unsafe fn i32_from_jobject(obj: jobject, jni_env: *mut JNIEnv) -> errors::Result<i32> {
381    if obj.is_null() {
382        Err(errors::J4RsError::JniError(
383            "Attempt to create an i32 from null".to_string(),
384        ))
385    } else {
386        let v = (opt_to_res(cache::get_jni_call_int_method())?)(
387            jni_env,
388            obj,
389            cache::get_integer_to_int_method()?,
390        );
391        Ok(v as i32)
392    }
393}
394
395pub(crate) fn global_jobject_from_i64(a: &i64, jni_env: *mut JNIEnv) -> errors::Result<jobject> {
396    unsafe {
397        let tmp = *a;
398        let o = (opt_to_res(cache::get_jni_new_object())?)(
399            jni_env,
400            cache::get_long_class()?,
401            cache::get_long_constructor_method()?,
402            tmp as *const i64,
403        );
404        create_global_ref_from_local_ref(o, jni_env)
405    }
406}
407
408pub(crate) unsafe fn i64_from_jobject(obj: jobject, jni_env: *mut JNIEnv) -> errors::Result<i64> {
409    if obj.is_null() {
410        Err(errors::J4RsError::JniError(
411            "Attempt to create an i64 from null".to_string(),
412        ))
413    } else {
414        let v = (opt_to_res(cache::get_jni_call_long_method())?)(
415            jni_env,
416            obj,
417            cache::get_long_to_long_method()?,
418        );
419        Ok(v as i64)
420    }
421}
422
423pub(crate) fn global_jobject_from_f32(a: &f32, jni_env: *mut JNIEnv) -> errors::Result<jobject> {
424    let tmp = *a;
425    unsafe {
426        let o = (opt_to_res(cache::get_jni_new_object())?)(
427            jni_env,
428            cache::get_float_class()?,
429            cache::get_float_constructor_method()?,
430            tmp as c_double,
431        );
432        create_global_ref_from_local_ref(o, jni_env)
433    }
434}
435
436pub(crate) unsafe fn f32_from_jobject(obj: jobject, jni_env: *mut JNIEnv) -> errors::Result<f32> {
437    if obj.is_null() {
438        Err(errors::J4RsError::JniError(
439            "Attempt to create an f32 from null".to_string(),
440        ))
441    } else {
442        let v = (opt_to_res(cache::get_jni_call_float_method())?)(
443            jni_env,
444            obj,
445            cache::get_float_to_float_method()?,
446        );
447        Ok(v)
448    }
449}
450
451pub(crate) fn global_jobject_from_f64(a: &f64, jni_env: *mut JNIEnv) -> errors::Result<jobject> {
452    let tmp = *a;
453    unsafe {
454        let o = (opt_to_res(cache::get_jni_new_object())?)(
455            jni_env,
456            cache::get_double_class()?,
457            cache::get_double_constructor_method()?,
458            tmp as c_double,
459        );
460        create_global_ref_from_local_ref(o, jni_env)
461    }
462}
463
464pub(crate) unsafe fn f64_from_jobject(obj: jobject, jni_env: *mut JNIEnv) -> errors::Result<f64> {
465    if obj.is_null() {
466        Err(errors::J4RsError::JniError(
467            "Attempt to create an f64 from null".to_string(),
468        ))
469    } else {
470        let v = (opt_to_res(cache::get_jni_call_double_method())?)(
471            jni_env,
472            obj,
473            cache::get_double_to_double_method()?,
474        );
475        Ok(v)
476    }
477}
478
479macro_rules! primitive_array_from_jobject {
480    ($fn_name:ident, $rust_type:ty, $get_array_element:path, $release_array_element:path) => {
481        pub(crate) unsafe fn $fn_name(obj: jobject, jni_env: *mut JNIEnv) -> errors::Result<Vec<$rust_type>> {
482            if obj.is_null() {
483                Err(errors::J4RsError::JniError(
484                    format!("Attempt to create an {} array from null", stringify!($rust_type)),
485                ))
486            } else {
487                // length is at most 2^31-1, which should be smaller than the usize::MAX on a 32/64-bit host
488                let length = (opt_to_res(cache::get_jni_get_array_length())?)(
489                    jni_env,
490                    obj
491                ) as usize;
492                let val_ptr = (opt_to_res($get_array_element())?)(
493                    jni_env,
494                    obj,
495                    ptr::null_mut()
496                );
497                if val_ptr.is_null() { return Err(errors::J4RsError::JniError(format!("{} failed", stringify!($get_array_element)))) }
498                let total_bytes = length.checked_mul(mem::size_of::<$rust_type>()).expect("array bytes overflow");
499                let mut vec = Vec::<$rust_type>::with_capacity(length);
500                // `copy_nonoverlapping` requires that both src and dst are aligned. Since JVM does not
501                // enforce alignment, we copy the source buffer as bytes.
502                ptr::copy_nonoverlapping(val_ptr as *const u8, vec.as_mut_ptr() as *mut u8, total_bytes);
503                vec.set_len(length);
504                (opt_to_res($release_array_element())?)(
505                    jni_env,
506                    obj,
507                    val_ptr,
508                    jni_sys::JNI_ABORT,
509                );
510                Ok(vec)
511            }
512        }
513    };
514}
515
516primitive_array_from_jobject!(i8_array_from_jobject, i8, cache::get_jni_get_byte_array_elements, cache::get_jni_release_byte_array_elements);
517primitive_array_from_jobject!(i16_array_from_jobject, i16, cache::get_jni_get_short_array_elements, cache::get_jni_release_short_array_elements);
518primitive_array_from_jobject!(u16_array_from_jobject, u16, cache::get_jni_get_char_array_elements, cache::get_jni_release_char_array_elements);
519primitive_array_from_jobject!(i32_array_from_jobject, i32, cache::get_jni_get_int_array_elements, cache::get_jni_release_int_array_elements);
520primitive_array_from_jobject!(i64_array_from_jobject, i64, cache::get_jni_get_long_array_elements, cache::get_jni_release_long_array_elements);
521primitive_array_from_jobject!(f32_array_from_jobject, f32, cache::get_jni_get_float_array_elements, cache::get_jni_release_float_array_elements);
522primitive_array_from_jobject!(f64_array_from_jobject, f64, cache::get_jni_get_double_array_elements, cache::get_jni_release_double_array_elements);
523primitive_array_from_jobject!(boolean_array_from_jobject, bool, cache::get_jni_get_boolean_array_elements, cache::get_jni_release_boolean_array_elements);
524
525pub(crate) unsafe fn string_from_jobject(
526    obj: jobject,
527    jni_env: *mut JNIEnv,
528) -> errors::Result<String> {
529    if obj.is_null() {
530        Err(errors::J4RsError::JniError(
531            "Attempt to create a String from null".to_string(),
532        ))
533    } else {
534        let s = (opt_to_res(cache::get_jni_get_string_utf_chars())?)(jni_env, obj, ptr::null_mut())
535            as *mut c_char;
536        let rust_string = utils::to_rust_string(s)?;
537
538        Ok(rust_string)
539    }
540}
541
542pub unsafe fn jstring_to_rust_string(jvm: &Jvm, java_string: jstring) -> errors::Result<String> {
543    let s = (opt_to_res(cache::get_jni_get_string_utf_chars())?)(
544        jvm.jni_env,
545        java_string,
546        ptr::null_mut(),
547    ) as *mut c_char;
548    let rust_string = utils::to_rust_string(s)?;
549    (opt_to_res(cache::get_jni_release_string_utf_chars())?)(jvm.jni_env, java_string, s);
550    Jvm::do_return(jvm.jni_env, rust_string)
551}
552
553pub(crate) unsafe fn throw_exception(message: &str, jni_env: *mut JNIEnv) -> errors::Result<i32> {
554    let message_jstring = utils::to_c_string_struct(message);
555    let i = (opt_to_res(cache::get_jni_throw_new())?)(
556        jni_env,
557        cache::get_invocation_exception_class()?,
558        message_jstring.as_ptr(),
559    );
560    Ok(i)
561}