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
15pub struct JniBindgenConfig<'a> {
17 pub group_id: &'a str,
19 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
52pub 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 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 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 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 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 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 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 f.writeln("let _cache = get_cache();")?;
414
415 f.newline()?;
416
417 for param in &handle.arguments {
419 if let Some(converted) = param.arg_type.to_rust(¶m.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 let args = handle
436 .arguments
437 .iter()
438 .map(|param| {
439 param
440 .arg_type
441 .call_site(¶m.name)
442 .unwrap_or_else(|| param.name.to_string())
443 })
444 .chain(extra_param)
445 .collect::<Vec<String>>()
446 .join(", ");
447
448 let invocation = format!(
450 "unsafe {{ {}::ffi::{}_{}({}) }}",
451 config.ffi_name, lib.settings.c_ffi_prefix, handle.name, args
452 );
453
454 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 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 if !handle.return_type.is_none() {
517 f.writeln("return _result.into();")?;
518 }
519
520 Ok(())
521 })
522}