oo_bindgen/backend/java/jni/
mod.rs

1use crate::backend::*;
2use crate::model::*;
3
4use crate::backend::java::jni::conversion::*;
5
6use std::path::Path;
7
8mod classes;
9mod conversion;
10mod enums;
11mod exceptions;
12mod interface;
13mod structs;
14
15/// Configuration for JNI (Rust) generation
16pub struct JniBindgenConfig<'a> {
17    /// Maven group id (e.g. io.stepfunc)
18    pub group_id: &'a str,
19    /// Name of the FFI target
20    pub ffi_name: &'a str,
21}
22
23impl<'a> JniBindgenConfig<'a> {
24    fn java_signature_path(&self, libname: &str) -> String {
25        let mut result = self.group_id.replace('.', "/");
26        result.push('/');
27        result.push_str(libname);
28        result
29    }
30}
31
32fn module_string(name: &str, f: &mut dyn Printer, content: &str) -> FormattingResult<()> {
33    module(name, f, |f| {
34        for line in content.lines() {
35            f.writeln(line)?;
36        }
37        Ok(())
38    })
39}
40
41fn module<F>(name: &str, f: &mut dyn Printer, write: F) -> FormattingResult<()>
42where
43    F: Fn(&mut dyn Printer) -> FormattingResult<()>,
44{
45    f.newline()?;
46    f.writeln(&format!("pub(crate) mod {name} {{"))?;
47    indented(f, |f| write(f))?;
48    f.writeln("}")?;
49    Ok(())
50}
51
52/// Generate all of the JNI (Rust) source code that glues the Java and FFI together
53///
54/// This function is typically called from a build.rs script in a target that builds
55/// the JNI shared library
56pub fn generate_jni(path: &Path, lib: &Library, config: &JniBindgenConfig) -> FormattingResult<()> {
57    let mut f = FilePrinter::new(path)?;
58
59    generate_cache(&mut f)?;
60    write_functions(&mut f, lib, config)?;
61    write_collection_conversions(&mut f, lib, config)?;
62    write_iterator_conversions(&mut f, lib, config)?;
63
64    module("classes", &mut f, |f| {
65        classes::generate_classes_cache(f, lib, config)
66    })?;
67
68    module("enums", &mut f, |f| {
69        enums::generate_enums_cache(f, lib, config)
70    })?;
71
72    module("structs", &mut f, |f| structs::generate(f, lib, config))?;
73
74    module("interfaces", &mut f, |f| {
75        interface::generate_interfaces_cache(f, lib, config)
76    })?;
77
78    module("exceptions", &mut f, |f| {
79        exceptions::generate_exceptions_cache(f, lib, config)
80    })?;
81
82    // Copy the modules that never change
83    module_string("primitives", &mut f, include_str!("copy/primitives.rs"))?;
84    module_string("unsigned", &mut f, include_str!("copy/unsigned.rs"))?;
85    module_string("duration", &mut f, include_str!("copy/duration.rs"))?;
86    module_string("collection", &mut f, include_str!("copy/collection.rs"))?;
87    module_string("pointers", &mut f, include_str!("copy/pointers.rs"))?;
88    module_string("util", &mut f, include_str!("copy/util.rs"))?;
89
90    Ok(())
91}
92
93fn generate_cache(f: &mut dyn Printer) -> FormattingResult<()> {
94    // Create cache
95    f.writeln("pub(crate) struct JCache")?;
96    blocked(f, |f| {
97        f.writeln("vm: jni::JavaVM,")?;
98        f.writeln("primitives: primitives::Primitives,")?;
99        f.writeln("unsigned: unsigned::Unsigned,")?;
100        f.writeln("duration: duration::Duration,")?;
101        f.writeln("collection: collection::Collection,")?;
102        f.writeln("classes: classes::Classes,")?;
103        f.writeln("enums: enums::Enums,")?;
104        f.writeln("structs: structs::Structs,")?;
105        f.writeln("interfaces: interfaces::Interfaces,")?;
106        f.writeln("exceptions: exceptions::Exceptions,")?;
107        Ok(())
108    })?;
109
110    f.newline()?;
111
112    f.writeln("impl JCache")?;
113    blocked(f, |f| {
114        f.writeln("fn init(vm: jni::JavaVM) -> Self")?;
115        blocked(f, |f| {
116            f.writeln("let env = vm.get_env().unwrap();")?;
117            f.writeln("let primitives = primitives::Primitives::init(&env);")?;
118            f.writeln("let unsigned = unsigned::Unsigned::init(&env);")?;
119            f.writeln("let duration = duration::Duration::init(&env);")?;
120            f.writeln("let collection = collection::Collection::init(&env);")?;
121            f.writeln("let classes = classes::Classes::init(&env);")?;
122            f.writeln("let enums = enums::Enums::init(&env);")?;
123            f.writeln("let structs = structs::Structs::init(&env);")?;
124            f.writeln("let interfaces = interfaces::Interfaces::init(&env);")?;
125            f.writeln("let exceptions = exceptions::Exceptions::init(&env);")?;
126            f.writeln("Self")?;
127            blocked(f, |f| {
128                f.writeln("vm,")?;
129                f.writeln("primitives,")?;
130                f.writeln("unsigned,")?;
131                f.writeln("duration,")?;
132                f.writeln("collection,")?;
133                f.writeln("classes,")?;
134                f.writeln("enums,")?;
135                f.writeln("structs,")?;
136                f.writeln("interfaces,")?;
137                f.writeln("exceptions,")?;
138                Ok(())
139            })
140        })
141    })?;
142
143    f.newline()?;
144
145    f.writeln("static mut JCACHE: Option<JCache> = None;")?;
146
147    f.newline()?;
148
149    f.writeln("pub(crate) fn get_cache<'a>() -> &'a JCache {")?;
150    indented(f, |f| f.writeln("unsafe { JCACHE.as_ref().unwrap() }"))?;
151    f.writeln("}")?;
152
153    f.newline()?;
154
155    // OnLoad function
156    f.writeln("#[no_mangle]")?;
157    f.writeln("pub extern \"C\" fn JNI_OnLoad(vm: *mut jni::sys::JavaVM, _: *mut std::ffi::c_void) -> jni::sys::jint")?;
158    blocked(f, |f| {
159        f.writeln("let vm = unsafe { jni::JavaVM::from_raw(vm).unwrap() };")?;
160        f.writeln("let jcache = JCache::init(vm);")?;
161        f.writeln("unsafe { JCACHE.replace(jcache) };")?;
162        f.writeln("jni::JNIVersion::V8.into()")
163    })?;
164
165    f.newline()?;
166
167    // OnUnload function
168    f.writeln("#[no_mangle]")?;
169    f.writeln("pub extern \"C\" fn JNI_OnUnload(_vm: *mut jni::sys::JavaVM, _: *mut std::ffi::c_void) -> jni::sys::jint")?;
170    blocked(f, |f| {
171        f.writeln("unsafe { JCACHE.take().unwrap(); }")?;
172        f.writeln("return 0;")
173    })
174}
175
176fn write_collection_conversions(
177    f: &mut dyn Printer,
178    lib: &Library,
179    config: &JniBindgenConfig,
180) -> FormattingResult<()> {
181    f.newline()?;
182    f.writeln("/// convert Java lists into native API collections")?;
183    f.writeln("pub(crate) mod collections {")?;
184    indented(f, |f| {
185        for col in lib.collections() {
186            f.newline()?;
187            write_collection_guard(f, config, col)?;
188        }
189        Ok(())
190    })?;
191    f.writeln("}")
192}
193
194fn write_iterator_conversions(
195    f: &mut dyn Printer,
196    lib: &Library,
197    config: &JniBindgenConfig,
198) -> FormattingResult<()> {
199    f.newline()?;
200    f.writeln("/// functions that convert native API iterators into Java lists")?;
201    f.writeln("pub(crate) mod iterators {")?;
202    indented(f, |f| {
203        for iter in lib.iterators() {
204            f.newline()?;
205            write_iterator_conversion(f, config, iter)?;
206        }
207        Ok(())
208    })?;
209    f.writeln("}")
210}
211
212fn write_iterator_conversion(
213    f: &mut dyn Printer,
214    config: &JniBindgenConfig,
215    iter: &Handle<AbstractIterator<Validated>>,
216) -> FormattingResult<()> {
217    f.writeln(&format!("pub(crate) fn {}(_env: &jni::JNIEnv, _cache: &crate::JCache, iter: {}) -> jni::sys::jobject {{", iter.name(), iter.iter_class.get_rust_type(config.ffi_name)))?;
218    indented(f, |f| {
219        f.writeln("let list = _cache.collection.new_array_list(&_env);")?;
220        f.writeln(&format!(
221            "while let Some(next) = unsafe {{ {}::ffi::{}_{}(iter).as_ref() }} {{",
222            config.ffi_name, iter.iter_class.settings.c_ffi_prefix, iter.next_function.name
223        ))?;
224        indented(f, |f| {
225            match &iter.item_type {
226                IteratorItemType::Primitive(x) => {
227                    let converted = x
228                        .maybe_convert("*next")
229                        .unwrap_or_else(|| "*next".to_string());
230                    f.writeln(&format!("let next = _env.auto_local({converted});"))?;
231                }
232                IteratorItemType::Struct(x) => {
233                    f.writeln(&format!(
234                        "let next = _env.auto_local({});",
235                        x.convert("next")
236                    ))?;
237                }
238            }
239            f.writeln("_cache.collection.add_to_array_list(&_env, list, next.as_obj().into());")
240        })?;
241        f.writeln("}")?;
242        f.writeln("list.into_inner()")
243    })?;
244    f.writeln("}")
245}
246
247fn write_collection_guard(
248    f: &mut dyn Printer,
249    config: &JniBindgenConfig,
250    col: &Handle<Collection<Validated>>,
251) -> FormattingResult<()> {
252    let collection_name = col.collection_class.name.camel_case();
253    let c_ffi_prefix = col.collection_class.settings.c_ffi_prefix.clone();
254
255    f.writeln("/// Guard that builds the C collection type from a Java list")?;
256    f.writeln(&format!("pub(crate) struct {collection_name} {{"))?;
257    indented(f, |f| {
258        f.writeln(&format!(
259            "inner: *mut {}::{}",
260            config.ffi_name, collection_name
261        ))
262    })?;
263    f.writeln("}")?;
264
265    f.newline()?;
266
267    f.writeln(&format!("impl std::ops::Deref for {collection_name} {{"))?;
268    indented(f, |f| {
269        f.writeln(&format!(
270            "type Target = *mut {}::{};",
271            config.ffi_name, collection_name
272        ))?;
273        f.newline()?;
274        f.writeln("fn deref(&self) -> &Self::Target {")?;
275        indented(f, |f| f.writeln("&self.inner"))?;
276        f.writeln("}")
277    })?;
278    f.writeln("}")?;
279
280    f.newline()?;
281
282    f.writeln(&format!("impl {collection_name} {{"))?;
283    indented(f, |f| {
284        f.writeln("pub(crate) fn new(_env: jni::JNIEnv, list: jni::sys::jobject) -> Result<Self, jni::errors::Error> {")?;
285        indented(f, |f| {
286            f.writeln("let _cache = crate::get_cache();")?;
287            let size = if col.has_reserve {
288                f.writeln("let size = _cache.collection.get_size(&_env, list.into());")?;
289                "size"
290            } else {
291                ""
292            };
293            f.writeln(&format!(
294                "let col = Self {{ inner: unsafe {{ {}::ffi::{}_{}({}) }} }};",
295                config.ffi_name, c_ffi_prefix, col.create_func.name, size
296            ))?;
297            f.writeln(
298                "let it = _env.auto_local(_cache.collection.get_iterator(&_env, list.into()));",
299            )?;
300            f.writeln("while _cache.collection.has_next(&_env, it.as_obj()) {")?;
301            indented(f, |f| {
302                f.writeln(
303                    "let next = _env.auto_local(_cache.collection.next(&_env, it.as_obj()));",
304                )?;
305                if let Some(converted) = col
306                    .item_type
307                    .to_rust_from_object("next.as_obj().into_inner()")
308                {
309                    // perform  primary conversion that shadows the variable
310                    f.writeln(&format!("let next = {converted};"))?;
311                }
312                let arg = col
313                    .item_type
314                    .call_site("next")
315                    .unwrap_or_else(|| "next".to_string());
316                f.writeln(&format!(
317                    "unsafe {{ {}::ffi::{}_{}(col.inner, {}) }};",
318                    config.ffi_name, c_ffi_prefix, col.add_func.name, arg
319                ))?;
320                Ok(())
321            })?;
322            f.writeln("}")?;
323            f.writeln("Ok(col)")?;
324            Ok(())
325        })?;
326        f.writeln("}")
327    })?;
328    f.writeln("}")?;
329
330    f.newline()?;
331
332    f.writeln("/// Destroy the C collection on drop")?;
333    f.writeln(&format!("impl Drop for {collection_name} {{"))?;
334    indented(f, |f| {
335        f.writeln("fn drop(&mut self) {")?;
336        indented(f, |f| {
337            f.writeln(&format!(
338                "unsafe {{ {}::ffi::{}_{}(self.inner) }}",
339                config.ffi_name, c_ffi_prefix, col.delete_func.name
340            ))
341        })?;
342        f.writeln("}")
343    })?;
344    f.writeln("}")
345}
346
347fn write_functions(
348    f: &mut dyn Printer,
349    lib: &Library,
350    config: &JniBindgenConfig,
351) -> FormattingResult<()> {
352    fn skip(c: FunctionCategory) -> bool {
353        match c {
354            FunctionCategory::Native => false,
355            // these all get used internally to the JNI and
356            // don't need external wrappers accessed from Java
357            FunctionCategory::CollectionCreate => true,
358            FunctionCategory::CollectionDestroy => true,
359            FunctionCategory::CollectionAdd => true,
360            FunctionCategory::IteratorNext => true,
361        }
362    }
363
364    for handle in lib.functions().filter(|f| !skip(f.category)) {
365        f.newline()?;
366        write_function(f, lib, config, handle)?;
367    }
368    Ok(())
369}
370
371fn write_function_signature(
372    f: &mut dyn Printer,
373    lib: &Library,
374    config: &JniBindgenConfig,
375    handle: &Handle<Function<Validated>>,
376) -> FormattingResult<()> {
377    let args = handle
378        .arguments
379        .iter()
380        .map(|param| format!("{}: {}", param.name, param.arg_type.jni_signature_type()))
381        .collect::<Vec<String>>()
382        .join(", ");
383
384    let returns = match handle.return_type.get_value() {
385        None => "".to_string(),
386        Some(x) => {
387            format!(" -> {}", x.jni_signature_type())
388        }
389    };
390
391    f.writeln("#[no_mangle]")?;
392    f.writeln(
393        &format!(
394            "pub extern \"C\" fn Java_{}_{}_NativeFunctions_{}(_env: jni::JNIEnv, _: jni::sys::jobject, {}){}",
395            config.group_id.replace('.', "_"),
396            lib.settings.name,
397            handle.name.replace('_', "_1"),
398            args,
399            returns
400        )
401    )
402}
403
404fn write_function(
405    f: &mut dyn Printer,
406    lib: &Library,
407    config: &JniBindgenConfig,
408    handle: &Handle<Function<Validated>>,
409) -> FormattingResult<()> {
410    write_function_signature(f, lib, config, handle)?;
411    blocked(f, |f| {
412        // Get the JCache
413        f.writeln("let _cache = get_cache();")?;
414
415        f.newline()?;
416
417        // Perform the primary conversion of the parameters if required
418        for param in &handle.arguments {
419            if let Some(converted) = param.arg_type.to_rust(&param.name) {
420                let conversion = format!("let {} = {};", param.name, converted);
421                f.writeln(&conversion)?;
422            }
423        }
424
425        f.newline()?;
426
427        let extra_param = match handle.get_signature_type() {
428            SignatureType::NoErrorNoReturn => None,
429            SignatureType::NoErrorWithReturn(_, _) => None,
430            SignatureType::ErrorNoReturn(_) => None,
431            SignatureType::ErrorWithReturn(_, _, _) => Some("_out.as_mut_ptr()".to_string()),
432        };
433
434        // list of arguments in the invocation
435        let args = handle
436            .arguments
437            .iter()
438            .map(|param| {
439                param
440                    .arg_type
441                    .call_site(&param.name)
442                    .unwrap_or_else(|| param.name.to_string())
443            })
444            .chain(extra_param)
445            .collect::<Vec<String>>()
446            .join(", ");
447
448        // the invocation of the native function
449        let invocation = format!(
450            "unsafe {{ {}::ffi::{}_{}({}) }}",
451            config.ffi_name, lib.settings.c_ffi_prefix, handle.name, args
452        );
453
454        // Call the C FFI
455        match handle.get_signature_type() {
456            SignatureType::NoErrorNoReturn => {
457                f.writeln(&format!("{invocation};"))?;
458            }
459            SignatureType::NoErrorWithReturn(_, _) | SignatureType::ErrorNoReturn(_) => {
460                f.writeln(&format!("let _result = {invocation};"))?;
461            }
462            SignatureType::ErrorWithReturn(_, _, _) => {
463                f.writeln("let mut _out = std::mem::MaybeUninit::uninit();")?;
464                f.writeln(&format!("let _result = {invocation};"))?;
465            }
466        };
467
468        // Convert return value
469        match handle.get_signature_type() {
470            SignatureType::NoErrorNoReturn => (),
471            SignatureType::NoErrorWithReturn(return_type, _) => {
472                if let Some(conversion) = return_type.maybe_convert("_result") {
473                    f.writeln(&format!("let _result = {conversion};"))?;
474                }
475            }
476            SignatureType::ErrorNoReturn(error_type) => {
477                f.writeln("if _result != 0")?;
478                blocked(f, |f| {
479                    error_type.inner.convert("_result");
480                    f.writeln(&format!(
481                        "let _error = {};",
482                        error_type.inner.convert("_result")
483                    ))?;
484                    f.writeln(&format!(
485                        "let error = _cache.exceptions.{}.throw(&_env, _error);",
486                        error_type.exception_name
487                    ))
488                })?;
489            }
490            SignatureType::ErrorWithReturn(error_type, return_type, _) => {
491                f.writeln("let _result = if _result == 0")?;
492                blocked(f, |f| {
493                    f.writeln("let _result = unsafe { _out.assume_init() };")?;
494                    if let Some(conversion) = return_type.maybe_convert("_result") {
495                        f.writeln(&conversion)?;
496                    }
497                    Ok(())
498                })?;
499                f.writeln("else")?;
500                blocked(f, |f| {
501                    f.writeln(&format!(
502                        "let _error = {};",
503                        error_type.inner.convert("_result")
504                    ))?;
505                    f.writeln(&format!(
506                        "let error = _cache.exceptions.{}.throw(&_env, _error);",
507                        error_type.exception_name
508                    ))?;
509                    f.writeln(return_type.get_default_value())
510                })?;
511                f.write(";")?;
512            }
513        }
514
515        // Return value
516        if !handle.return_type.is_none() {
517            f.writeln("return _result.into();")?;
518        }
519
520        Ok(())
521    })
522}