1use crate::type_map::{java_boxed_type, java_ffi_type, java_type};
2use alef_codegen::naming::{to_class_name, to_java_name};
3use alef_core::backend::{Backend, BuildConfig, Capabilities, GeneratedFile};
4use alef_core::config::{AlefConfig, Language, resolve_output_dir};
5use alef_core::ir::{ApiSurface, EnumDef, FunctionDef, PrimitiveType, TypeDef, TypeRef};
6use heck::ToSnakeCase;
7use std::fmt::Write;
8use std::path::PathBuf;
9
10const JAVA_OBJECT_METHOD_NAMES: &[&str] = &[
13 "wait",
14 "notify",
15 "notifyAll",
16 "getClass",
17 "hashCode",
18 "equals",
19 "toString",
20 "clone",
21 "finalize",
22];
23
24fn safe_java_field_name(name: &str) -> String {
28 let java_name = to_java_name(name);
29 if JAVA_OBJECT_METHOD_NAMES.contains(&java_name.as_str()) {
30 format!("{}Value", java_name)
31 } else {
32 java_name
33 }
34}
35
36pub struct JavaBackend;
37
38impl JavaBackend {
39 fn resolve_main_class(api: &ApiSurface) -> String {
41 to_class_name(&api.crate_name.replace('-', "_"))
42 }
43}
44
45impl Backend for JavaBackend {
46 fn name(&self) -> &str {
47 "java"
48 }
49
50 fn language(&self) -> Language {
51 Language::Java
52 }
53
54 fn capabilities(&self) -> Capabilities {
55 Capabilities {
56 supports_async: true,
57 supports_classes: true,
58 supports_enums: true,
59 supports_option: true,
60 supports_result: true,
61 ..Capabilities::default()
62 }
63 }
64
65 fn generate_bindings(&self, api: &ApiSurface, config: &AlefConfig) -> anyhow::Result<Vec<GeneratedFile>> {
66 let package = config.java_package();
67 let prefix = config.ffi_prefix();
68 let main_class = Self::resolve_main_class(api);
69 let package_path = package.replace('.', "/");
70
71 let output_dir = resolve_output_dir(
72 config.output.java.as_ref(),
73 &config.crate_config.name,
74 "packages/java/src/main/java/",
75 );
76
77 let base_path = PathBuf::from(&output_dir).join(&package_path);
78
79 let mut files = Vec::new();
80
81 files.push(GeneratedFile {
83 path: base_path.join("NativeLib.java"),
84 content: gen_native_lib(api, config, &package, &prefix),
85 generated_header: true,
86 });
87
88 files.push(GeneratedFile {
90 path: base_path.join(format!("{}.java", main_class)),
91 content: gen_main_class(api, config, &package, &main_class, &prefix),
92 generated_header: true,
93 });
94
95 files.push(GeneratedFile {
97 path: base_path.join(format!("{}Exception.java", main_class)),
98 content: gen_exception_class(&package, &main_class),
99 generated_header: true,
100 });
101
102 for typ in &api.types {
104 if !typ.is_opaque && !typ.fields.is_empty() {
105 files.push(GeneratedFile {
106 path: base_path.join(format!("{}.java", typ.name)),
107 content: gen_record_type(&package, typ),
108 generated_header: true,
109 });
110 if typ.has_default {
112 files.push(GeneratedFile {
113 path: base_path.join(format!("{}Builder.java", typ.name)),
114 content: gen_builder_class(&package, typ),
115 generated_header: true,
116 });
117 }
118 }
119 }
120
121 for typ in &api.types {
123 if typ.is_opaque {
124 files.push(GeneratedFile {
125 path: base_path.join(format!("{}.java", typ.name)),
126 content: gen_opaque_handle_class(&package, typ, &prefix),
127 generated_header: true,
128 });
129 }
130 }
131
132 for enum_def in &api.enums {
134 files.push(GeneratedFile {
135 path: base_path.join(format!("{}.java", enum_def.name)),
136 content: gen_enum_class(&package, enum_def),
137 generated_header: true,
138 });
139 }
140
141 for error in &api.errors {
143 for (class_name, content) in alef_codegen::error_gen::gen_java_error_types(error, &package) {
144 files.push(GeneratedFile {
145 path: base_path.join(format!("{}.java", class_name)),
146 content,
147 generated_header: true,
148 });
149 }
150 }
151
152 let _adapter_bodies = alef_adapters::build_adapter_bodies(config, Language::Java)?;
154
155 Ok(files)
156 }
157
158 fn generate_public_api(&self, api: &ApiSurface, config: &AlefConfig) -> anyhow::Result<Vec<GeneratedFile>> {
159 let package = config.java_package();
160 let prefix = config.ffi_prefix();
161 let main_class = Self::resolve_main_class(api);
162 let package_path = package.replace('.', "/");
163
164 let output_dir = resolve_output_dir(
165 config.output.java.as_ref(),
166 &config.crate_config.name,
167 "packages/java/src/main/java/",
168 );
169
170 let base_path = PathBuf::from(&output_dir).join(&package_path);
171
172 let public_class = main_class.trim_end_matches("Rs").to_string();
175 let facade_content = gen_facade_class(api, &package, &public_class, &main_class, &prefix);
176
177 Ok(vec![GeneratedFile {
178 path: base_path.join(format!("{}.java", public_class)),
179 content: facade_content,
180 generated_header: true,
181 }])
182 }
183
184 fn build_config(&self) -> Option<BuildConfig> {
185 Some(BuildConfig {
186 tool: "mvn",
187 crate_suffix: "",
188 depends_on_ffi: true,
189 post_build: vec![],
190 })
191 }
192}
193
194fn gen_native_lib(api: &ApiSurface, config: &AlefConfig, package: &str, prefix: &str) -> String {
199 let mut body = String::with_capacity(2048);
201 let lib_name = config.ffi_lib_name();
204
205 writeln!(body, "final class NativeLib {{").ok();
206 writeln!(body, " private static final Linker LINKER = Linker.nativeLinker();").ok();
207 writeln!(body, " private static final SymbolLookup LIB;").ok();
208 writeln!(body).ok();
209 writeln!(body, " static {{").ok();
210 writeln!(body, " System.loadLibrary(\"{}\");", lib_name).ok();
211 writeln!(body, " LIB = SymbolLookup.loaderLookup();").ok();
212 writeln!(body, " }}").ok();
213 writeln!(body).ok();
214
215 for func in &api.functions {
217 if !func.is_async {
218 let ffi_name = format!("{}_{}", prefix, func.name.to_lowercase());
219 let return_layout = gen_ffi_layout(&func.return_type);
220 let param_layouts: Vec<String> = func.params.iter().map(|p| gen_ffi_layout(&p.ty)).collect();
221
222 let layout_str = gen_function_descriptor(&return_layout, ¶m_layouts);
223
224 let handle_name = format!("{}_{}", prefix.to_uppercase(), func.name.to_uppercase());
225
226 writeln!(
227 body,
228 " static final MethodHandle {} = LINKER.downcallHandle(",
229 handle_name
230 )
231 .ok();
232 writeln!(body, " LIB.find(\"{}\").orElseThrow(),", ffi_name).ok();
233 writeln!(body, " {}", layout_str).ok();
234 writeln!(body, " );").ok();
235 }
236 }
237
238 {
240 let free_name = format!("{}_free_string", prefix);
241 let handle_name = format!("{}_FREE_STRING", prefix.to_uppercase());
242 writeln!(body).ok();
243 writeln!(
244 body,
245 " static final MethodHandle {} = LINKER.downcallHandle(",
246 handle_name
247 )
248 .ok();
249 writeln!(body, " LIB.find(\"{}\").orElseThrow(),", free_name).ok();
250 writeln!(body, " FunctionDescriptor.ofVoid(ValueLayout.ADDRESS)").ok();
251 writeln!(body, " );").ok();
252 }
253
254 {
256 writeln!(
257 body,
258 " static final MethodHandle {}_LAST_ERROR_CODE = LINKER.downcallHandle(",
259 prefix.to_uppercase()
260 )
261 .ok();
262 writeln!(body, " LIB.find(\"{}_last_error_code\").orElseThrow(),", prefix).ok();
263 writeln!(body, " FunctionDescriptor.of(ValueLayout.JAVA_INT)").ok();
264 writeln!(body, " );").ok();
265
266 writeln!(
267 body,
268 " static final MethodHandle {}_LAST_ERROR_CONTEXT = LINKER.downcallHandle(",
269 prefix.to_uppercase()
270 )
271 .ok();
272 writeln!(
273 body,
274 " LIB.find(\"{}_last_error_context\").orElseThrow(),",
275 prefix
276 )
277 .ok();
278 writeln!(body, " FunctionDescriptor.of(ValueLayout.ADDRESS)").ok();
279 writeln!(body, " );").ok();
280 }
281
282 for func in &api.functions {
284 if let TypeRef::Named(name) = &func.return_type {
285 let type_snake = name.to_snake_case();
286 let type_upper = type_snake.to_uppercase();
287
288 let content_handle = format!("{}_{}_CONTENT", prefix.to_uppercase(), type_upper);
290 let content_ffi = format!("{}_{}_content", prefix, type_snake);
291 writeln!(body).ok();
292 writeln!(
293 body,
294 " static final MethodHandle {} = LINKER.downcallHandle(",
295 content_handle
296 )
297 .ok();
298 writeln!(body, " LIB.find(\"{}\").orElseThrow(),", content_ffi).ok();
299 writeln!(
300 body,
301 " FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.ADDRESS)"
302 )
303 .ok();
304 writeln!(body, " );").ok();
305
306 let free_handle = format!("{}_{}_FREE", prefix.to_uppercase(), type_upper);
308 let free_ffi = format!("{}_{}_free", prefix, type_snake);
309 writeln!(
310 body,
311 " static final MethodHandle {} = LINKER.downcallHandle(",
312 free_handle
313 )
314 .ok();
315 writeln!(body, " LIB.find(\"{}\").orElseThrow(),", free_ffi).ok();
316 writeln!(body, " FunctionDescriptor.ofVoid(ValueLayout.ADDRESS)").ok();
317 writeln!(body, " );").ok();
318 }
319 }
320
321 writeln!(body, "}}").ok();
322
323 let mut out = String::with_capacity(body.len() + 512);
325
326 writeln!(out, "// DO NOT EDIT - auto-generated by alef").ok();
327 writeln!(out, "package {};", package).ok();
328 writeln!(out).ok();
329 if body.contains("Arena") {
330 writeln!(out, "import java.lang.foreign.Arena;").ok();
331 }
332 if body.contains("FunctionDescriptor") {
333 writeln!(out, "import java.lang.foreign.FunctionDescriptor;").ok();
334 }
335 if body.contains("Linker") {
336 writeln!(out, "import java.lang.foreign.Linker;").ok();
337 }
338 if body.contains("MemorySegment") {
339 writeln!(out, "import java.lang.foreign.MemorySegment;").ok();
340 }
341 if body.contains("SymbolLookup") {
342 writeln!(out, "import java.lang.foreign.SymbolLookup;").ok();
343 }
344 if body.contains("ValueLayout") {
345 writeln!(out, "import java.lang.foreign.ValueLayout;").ok();
346 }
347 if body.contains("MethodHandle") {
348 writeln!(out, "import java.lang.invoke.MethodHandle;").ok();
349 }
350 writeln!(out).ok();
351
352 out.push_str(&body);
353
354 out
355}
356
357fn gen_main_class(api: &ApiSurface, _config: &AlefConfig, package: &str, class_name: &str, prefix: &str) -> String {
362 let mut body = String::with_capacity(4096);
364
365 writeln!(body, "public final class {} {{", class_name).ok();
366 writeln!(body, " private {}() {{ }}", class_name).ok();
367 writeln!(body).ok();
368
369 for func in &api.functions {
371 gen_sync_function_method(&mut body, func, prefix, class_name);
373 writeln!(body).ok();
374
375 if func.is_async {
377 gen_async_wrapper_method(&mut body, func);
378 writeln!(body).ok();
379 }
380 }
381
382 gen_helper_methods(&mut body);
384
385 writeln!(body, "}}").ok();
386
387 let mut out = String::with_capacity(body.len() + 512);
389
390 writeln!(out, "// DO NOT EDIT - auto-generated by alef").ok();
391 writeln!(out, "package {};", package).ok();
392 writeln!(out).ok();
393 if body.contains("Arena") {
394 writeln!(out, "import java.lang.foreign.Arena;").ok();
395 }
396 if body.contains("FunctionDescriptor") {
397 writeln!(out, "import java.lang.foreign.FunctionDescriptor;").ok();
398 }
399 if body.contains("Linker") {
400 writeln!(out, "import java.lang.foreign.Linker;").ok();
401 }
402 if body.contains("MemorySegment") {
403 writeln!(out, "import java.lang.foreign.MemorySegment;").ok();
404 }
405 if body.contains("SymbolLookup") {
406 writeln!(out, "import java.lang.foreign.SymbolLookup;").ok();
407 }
408 if body.contains("ValueLayout") {
409 writeln!(out, "import java.lang.foreign.ValueLayout;").ok();
410 }
411 if body.contains("List<") {
412 writeln!(out, "import java.util.List;").ok();
413 }
414 if body.contains("Map<") {
415 writeln!(out, "import java.util.Map;").ok();
416 }
417 if body.contains("Optional<") {
418 writeln!(out, "import java.util.Optional;").ok();
419 }
420 if body.contains("HashMap<") || body.contains("new HashMap") {
421 writeln!(out, "import java.util.HashMap;").ok();
422 }
423 if body.contains("CompletableFuture") {
424 writeln!(out, "import java.util.concurrent.CompletableFuture;").ok();
425 }
426 if body.contains("CompletionException") {
427 writeln!(out, "import java.util.concurrent.CompletionException;").ok();
428 }
429 if body.contains("ObjectMapper") || body.contains("readValue") {
430 writeln!(out, "import com.fasterxml.jackson.databind.ObjectMapper;").ok();
431 }
432 writeln!(out).ok();
433
434 out.push_str(&body);
435
436 out
437}
438
439fn gen_sync_function_method(out: &mut String, func: &FunctionDef, prefix: &str, class_name: &str) {
440 let params: Vec<String> = func
441 .params
442 .iter()
443 .map(|p| {
444 let ptype = java_type(&p.ty);
445 format!("{} {}", ptype, to_java_name(&p.name))
446 })
447 .collect();
448
449 let return_type = java_type(&func.return_type);
450
451 writeln!(
452 out,
453 " public static {} {}({}) throws {}Exception {{",
454 return_type,
455 to_java_name(&func.name),
456 params.join(", "),
457 class_name
458 )
459 .ok();
460
461 writeln!(out, " try (var arena = Arena.ofConfined()) {{").ok();
462
463 for param in &func.params {
465 marshal_param_to_ffi(out, &to_java_name(¶m.name), ¶m.ty);
466 }
467
468 let ffi_handle = format!("NativeLib.{}_{}", prefix.to_uppercase(), func.name.to_uppercase());
470
471 let call_args: Vec<String> = func
472 .params
473 .iter()
474 .map(|p| ffi_param_name(&to_java_name(&p.name), &p.ty))
475 .collect();
476
477 if matches!(func.return_type, TypeRef::Unit) {
478 writeln!(out, " {}.invoke({});", ffi_handle, call_args.join(", ")).ok();
479 writeln!(out, " }} catch (Throwable e) {{").ok();
480 writeln!(
481 out,
482 " throw new {}Exception(\"FFI call failed\", e);",
483 class_name
484 )
485 .ok();
486 writeln!(out, " }}").ok();
487 } else if is_ffi_string_return(&func.return_type) {
488 let free_handle = format!("NativeLib.{}_FREE_STRING", prefix.to_uppercase());
489 writeln!(
490 out,
491 " var resultPtr = (MemorySegment) {}.invoke({});",
492 ffi_handle,
493 call_args.join(", ")
494 )
495 .ok();
496 writeln!(out, " if (resultPtr.equals(MemorySegment.NULL)) {{").ok();
497 writeln!(out, " return null;").ok();
498 writeln!(out, " }}").ok();
499 writeln!(
500 out,
501 " String result = resultPtr.reinterpret(Long.MAX_VALUE).getString(0);"
502 )
503 .ok();
504 writeln!(out, " {}.invoke(resultPtr);", free_handle).ok();
505 writeln!(out, " return result;").ok();
506 writeln!(out, " }} catch (Throwable e) {{").ok();
507 writeln!(
508 out,
509 " throw new {}Exception(\"FFI call failed\", e);",
510 class_name
511 )
512 .ok();
513 writeln!(out, " }}").ok();
514 } else if matches!(func.return_type, TypeRef::Named(_)) {
515 let return_type_name = match &func.return_type {
517 TypeRef::Named(name) => name,
518 _ => unreachable!(),
519 };
520 let type_snake = return_type_name.to_snake_case();
521 let free_handle = format!("NativeLib.{}_{}_FREE", prefix.to_uppercase(), type_snake.to_uppercase());
522 let content_handle = format!(
523 "NativeLib.{}_{}_CONTENT",
524 prefix.to_uppercase(),
525 type_snake.to_uppercase()
526 );
527 writeln!(
528 out,
529 " var resultPtr = (MemorySegment) {}.invoke({});",
530 ffi_handle,
531 call_args.join(", ")
532 )
533 .ok();
534 writeln!(out, " if (resultPtr.equals(MemorySegment.NULL)) {{").ok();
535 writeln!(out, " return null;").ok();
536 writeln!(out, " }}").ok();
537 writeln!(
539 out,
540 " var contentPtr = (MemorySegment) {}.invoke(resultPtr);",
541 content_handle
542 )
543 .ok();
544 writeln!(
545 out,
546 " String content = contentPtr.equals(MemorySegment.NULL) ? null :"
547 )
548 .ok();
549 writeln!(
550 out,
551 " contentPtr.reinterpret(Long.MAX_VALUE).getString(0);"
552 )
553 .ok();
554 writeln!(out, " {}.invoke(resultPtr);", free_handle).ok();
555 writeln!(out, " return new {}(", return_type_name).ok();
557 writeln!(out, " java.util.Optional.ofNullable(content),").ok();
558 writeln!(out, " java.util.Optional.empty(),").ok();
559 writeln!(out, " java.util.List.of(),").ok();
560 writeln!(out, " java.util.List.of()").ok();
561 writeln!(out, " );").ok();
562 writeln!(out, " }} catch (Throwable e) {{").ok();
563 writeln!(
564 out,
565 " throw new {}Exception(\"FFI call failed\", e);",
566 class_name
567 )
568 .ok();
569 writeln!(out, " }}").ok();
570 } else {
571 writeln!(
572 out,
573 " return ({}) {}.invoke({});",
574 java_ffi_return_cast(&func.return_type),
575 ffi_handle,
576 call_args.join(", ")
577 )
578 .ok();
579 writeln!(out, " }} catch (Throwable e) {{").ok();
580 writeln!(
581 out,
582 " throw new {}Exception(\"FFI call failed\", e);",
583 class_name
584 )
585 .ok();
586 writeln!(out, " }}").ok();
587 }
588
589 writeln!(out, " }}").ok();
590}
591
592fn gen_async_wrapper_method(out: &mut String, func: &FunctionDef) {
593 let params: Vec<String> = func
594 .params
595 .iter()
596 .map(|p| {
597 let ptype = java_type(&p.ty);
598 format!("{} {}", ptype, to_java_name(&p.name))
599 })
600 .collect();
601
602 let return_type = match &func.return_type {
603 TypeRef::Unit => "Void".to_string(),
604 other => java_boxed_type(other).to_string(),
605 };
606
607 let sync_method_name = to_java_name(&func.name);
608 let async_method_name = format!("{}Async", sync_method_name);
609 let param_names: Vec<String> = func.params.iter().map(|p| to_java_name(&p.name)).collect();
610
611 writeln!(
612 out,
613 " public static CompletableFuture<{}> {}({}) {{",
614 return_type,
615 async_method_name,
616 params.join(", ")
617 )
618 .ok();
619 writeln!(out, " return CompletableFuture.supplyAsync(() -> {{").ok();
620 writeln!(out, " try {{").ok();
621 writeln!(
622 out,
623 " return {}({});",
624 sync_method_name,
625 param_names.join(", ")
626 )
627 .ok();
628 writeln!(out, " }} catch (Throwable e) {{").ok();
629 writeln!(out, " throw new CompletionException(e);").ok();
630 writeln!(out, " }}").ok();
631 writeln!(out, " }});").ok();
632 writeln!(out, " }}").ok();
633}
634
635fn gen_exception_class(package: &str, class_name: &str) -> String {
640 let mut out = String::with_capacity(512);
641
642 writeln!(out, "// DO NOT EDIT - auto-generated by alef").ok();
643 writeln!(out, "package {};", package).ok();
644 writeln!(out).ok();
645
646 writeln!(out, "public class {}Exception extends Exception {{", class_name).ok();
647 writeln!(out, " private final int code;").ok();
648 writeln!(out).ok();
649 writeln!(out, " public {}Exception(int code, String message) {{", class_name).ok();
650 writeln!(out, " super(message);").ok();
651 writeln!(out, " this.code = code;").ok();
652 writeln!(out, " }}").ok();
653 writeln!(out).ok();
654 writeln!(
655 out,
656 " public {}Exception(String message, Throwable cause) {{",
657 class_name
658 )
659 .ok();
660 writeln!(out, " super(message, cause);").ok();
661 writeln!(out, " this.code = -1;").ok();
662 writeln!(out, " }}").ok();
663 writeln!(out).ok();
664 writeln!(out, " public int getCode() {{").ok();
665 writeln!(out, " return code;").ok();
666 writeln!(out, " }}").ok();
667 writeln!(out, "}}").ok();
668
669 out
670}
671
672fn gen_facade_class(api: &ApiSurface, package: &str, public_class: &str, raw_class: &str, _prefix: &str) -> String {
677 let mut body = String::with_capacity(4096);
678
679 writeln!(body, "public final class {} {{", public_class).ok();
680 writeln!(body, " private {}() {{ }}", public_class).ok();
681 writeln!(body).ok();
682
683 for func in &api.functions {
685 let params: Vec<String> = func
687 .params
688 .iter()
689 .map(|p| {
690 let ptype = java_type(&p.ty);
691 format!("{} {}", ptype, to_java_name(&p.name))
692 })
693 .collect();
694
695 let return_type = java_type(&func.return_type);
696
697 if !func.doc.is_empty() {
698 writeln!(body, " /**").ok();
699 for line in func.doc.lines() {
700 writeln!(body, " * {}", line).ok();
701 }
702 writeln!(body, " */").ok();
703 }
704
705 writeln!(
706 body,
707 " public static {} {}({}) throws {}Exception {{",
708 return_type,
709 to_java_name(&func.name),
710 params.join(", "),
711 raw_class
712 )
713 .ok();
714
715 for param in &func.params {
717 if !param.optional {
718 let pname = to_java_name(¶m.name);
719 writeln!(
720 body,
721 " java.util.Objects.requireNonNull({}, \"{} must not be null\");",
722 pname, pname
723 )
724 .ok();
725 }
726 }
727
728 let call_args: Vec<String> = func.params.iter().map(|p| to_java_name(&p.name)).collect();
730
731 if matches!(func.return_type, TypeRef::Unit) {
732 writeln!(
733 body,
734 " {}.{}({});",
735 raw_class,
736 to_java_name(&func.name),
737 call_args.join(", ")
738 )
739 .ok();
740 } else {
741 writeln!(
742 body,
743 " return {}.{}({});",
744 raw_class,
745 to_java_name(&func.name),
746 call_args.join(", ")
747 )
748 .ok();
749 }
750
751 writeln!(body, " }}").ok();
752 writeln!(body).ok();
753
754 let has_optional = func.params.iter().any(|p| p.optional);
756 if has_optional {
757 let required_params: Vec<String> = func
758 .params
759 .iter()
760 .filter(|p| !p.optional)
761 .map(|p| {
762 let ptype = java_type(&p.ty);
763 format!("{} {}", ptype, to_java_name(&p.name))
764 })
765 .collect();
766
767 writeln!(
768 body,
769 " public static {} {}({}) throws {}Exception {{",
770 return_type,
771 to_java_name(&func.name),
772 required_params.join(", "),
773 raw_class
774 )
775 .ok();
776
777 let full_args: Vec<String> = func
779 .params
780 .iter()
781 .map(|p| {
782 if p.optional {
783 "null".to_string()
784 } else {
785 to_java_name(&p.name)
786 }
787 })
788 .collect();
789
790 if matches!(func.return_type, TypeRef::Unit) {
791 writeln!(body, " {}({});", to_java_name(&func.name), full_args.join(", ")).ok();
792 } else {
793 writeln!(
794 body,
795 " return {}({});",
796 to_java_name(&func.name),
797 full_args.join(", ")
798 )
799 .ok();
800 }
801
802 writeln!(body, " }}").ok();
803 writeln!(body).ok();
804 }
805 }
806
807 writeln!(body, "}}").ok();
808
809 let mut out = String::with_capacity(body.len() + 512);
811
812 writeln!(out, "// DO NOT EDIT - auto-generated by alef").ok();
813 writeln!(out, "package {};", package).ok();
814 writeln!(out).ok();
815
816 if body.contains("List<") {
818 writeln!(out, "import java.util.List;").ok();
819 }
820 if body.contains("Map<") {
821 writeln!(out, "import java.util.Map;").ok();
822 }
823 if body.contains("Optional<") {
824 writeln!(out, "import java.util.Optional;").ok();
825 }
826
827 writeln!(out).ok();
828 out.push_str(&body);
829
830 out
831}
832
833fn gen_opaque_handle_class(package: &str, typ: &TypeDef, prefix: &str) -> String {
838 let mut out = String::with_capacity(1024);
839 let class_name = &typ.name;
840 let type_snake = class_name.to_snake_case();
841
842 writeln!(out, "// DO NOT EDIT - auto-generated by alef").ok();
843 writeln!(out, "package {};", package).ok();
844 writeln!(out).ok();
845 writeln!(out, "import java.lang.foreign.Arena;").ok();
846 writeln!(out, "import java.lang.foreign.MemorySegment;").ok();
847 writeln!(out).ok();
848
849 if !typ.doc.is_empty() {
850 writeln!(out, "/**").ok();
851 for line in typ.doc.lines() {
852 writeln!(out, " * {}", line).ok();
853 }
854 writeln!(out, " */").ok();
855 }
856
857 writeln!(out, "public class {} implements AutoCloseable {{", class_name).ok();
858 writeln!(out, " private final MemorySegment handle;").ok();
859 writeln!(out).ok();
860 writeln!(out, " {}(MemorySegment handle) {{", class_name).ok();
861 writeln!(out, " this.handle = handle;").ok();
862 writeln!(out, " }}").ok();
863 writeln!(out).ok();
864 writeln!(out, " MemorySegment handle() {{").ok();
865 writeln!(out, " return this.handle;").ok();
866 writeln!(out, " }}").ok();
867 writeln!(out).ok();
868 writeln!(out, " @Override").ok();
869 writeln!(out, " public void close() {{").ok();
870 writeln!(
871 out,
872 " if (handle != null && !handle.equals(MemorySegment.NULL)) {{"
873 )
874 .ok();
875 writeln!(out, " try {{").ok();
876 writeln!(
877 out,
878 " NativeLib.{}.invoke(handle);",
879 format!("{}_{}_FREE", prefix.to_uppercase(), type_snake.to_uppercase())
880 )
881 .ok();
882 writeln!(out, " }} catch (Throwable e) {{").ok();
883 writeln!(
884 out,
885 " throw new RuntimeException(\"Failed to free {}: \" + e.getMessage(), e);",
886 class_name
887 )
888 .ok();
889 writeln!(out, " }}").ok();
890 writeln!(out, " }}").ok();
891 writeln!(out, " }}").ok();
892 writeln!(out, "}}").ok();
893
894 out
895}
896
897const RECORD_LINE_WRAP_THRESHOLD: usize = 100;
904
905fn gen_record_type(package: &str, typ: &TypeDef) -> String {
906 let field_list: Vec<String> = typ
908 .fields
909 .iter()
910 .map(|f| {
911 let ftype = if f.optional {
912 format!("Optional<{}>", java_boxed_type(&f.ty))
913 } else {
914 java_type(&f.ty).to_string()
915 };
916 format!("{} {}", ftype, safe_java_field_name(&f.name))
917 })
918 .collect();
919
920 let single_line = format!("public record {}({}) {{ }}", typ.name, field_list.join(", "));
922
923 let mut record_block = String::new();
925 if single_line.len() > RECORD_LINE_WRAP_THRESHOLD && field_list.len() > 1 {
926 writeln!(record_block, "public record {}(", typ.name).ok();
927 for (i, field) in field_list.iter().enumerate() {
928 let comma = if i < field_list.len() - 1 { "," } else { "" };
929 writeln!(record_block, " {}{}", field, comma).ok();
930 }
931 writeln!(record_block, ") {{").ok();
932 } else {
933 writeln!(record_block, "public record {}({}) {{", typ.name, field_list.join(", ")).ok();
934 }
935
936 if typ.has_default {
938 writeln!(record_block, " public static {}Builder builder() {{", typ.name).ok();
939 writeln!(record_block, " return new {}Builder();", typ.name).ok();
940 writeln!(record_block, " }}").ok();
941 }
942
943 writeln!(record_block, "}}").ok();
944
945 let mut out = String::with_capacity(record_block.len() + 512);
947 writeln!(out, "// DO NOT EDIT - auto-generated by alef").ok();
948 writeln!(out, "package {};", package).ok();
949 writeln!(out).ok();
950 if single_line.contains("List<") {
951 writeln!(out, "import java.util.List;").ok();
952 }
953 if single_line.contains("Map<") {
954 writeln!(out, "import java.util.Map;").ok();
955 }
956 if single_line.contains("Optional<") {
957 writeln!(out, "import java.util.Optional;").ok();
958 }
959 writeln!(out).ok();
960 write!(out, "{}", record_block).ok();
961
962 out
963}
964
965fn gen_enum_class(package: &str, enum_def: &EnumDef) -> String {
970 let mut out = String::with_capacity(1024);
971
972 writeln!(out, "// DO NOT EDIT - auto-generated by alef").ok();
973 writeln!(out, "package {};", package).ok();
974 writeln!(out).ok();
975
976 writeln!(out, "public enum {} {{", enum_def.name).ok();
977
978 for (i, variant) in enum_def.variants.iter().enumerate() {
979 let comma = if i < enum_def.variants.len() - 1 { "," } else { ";" };
980 writeln!(out, " {}{}", variant.name, comma).ok();
981 }
982
983 writeln!(out, "}}").ok();
984
985 out
986}
987
988fn gen_ffi_layout(ty: &TypeRef) -> String {
993 match ty {
994 TypeRef::Primitive(prim) => java_ffi_type(prim).to_string(),
995 TypeRef::String | TypeRef::Char | TypeRef::Path | TypeRef::Json => "ValueLayout.ADDRESS".to_string(),
996 TypeRef::Bytes => "ValueLayout.ADDRESS".to_string(),
997 TypeRef::Optional(inner) => gen_ffi_layout(inner),
998 TypeRef::Vec(_) => "ValueLayout.ADDRESS".to_string(),
999 TypeRef::Map(_, _) => "ValueLayout.ADDRESS".to_string(),
1000 TypeRef::Named(_) => "ValueLayout.ADDRESS".to_string(),
1001 TypeRef::Unit => "".to_string(),
1002 TypeRef::Duration => "ValueLayout.JAVA_LONG".to_string(),
1003 }
1004}
1005
1006fn marshal_param_to_ffi(out: &mut String, name: &str, ty: &TypeRef) {
1007 match ty {
1008 TypeRef::String | TypeRef::Char | TypeRef::Path | TypeRef::Json => {
1009 let cname = "c".to_string() + name;
1010 writeln!(out, " var {} = arena.allocateFrom({});", cname, name).ok();
1011 }
1012 TypeRef::Named(_) => {
1013 let cname = "c".to_string() + name;
1016 writeln!(out, " var {} = MemorySegment.NULL;", cname).ok();
1017 }
1018 TypeRef::Optional(inner) => {
1019 match inner.as_ref() {
1021 TypeRef::String | TypeRef::Char | TypeRef::Path | TypeRef::Json => {
1022 let cname = "c".to_string() + name;
1023 writeln!(
1024 out,
1025 " var {} = {} != null ? arena.allocateFrom({}) : MemorySegment.NULL;",
1026 cname, name, name
1027 )
1028 .ok();
1029 }
1030 TypeRef::Named(_) => {
1031 let cname = "c".to_string() + name;
1033 writeln!(out, " var {} = MemorySegment.NULL;", cname).ok();
1034 }
1035 _ => {
1036 }
1038 }
1039 }
1040 _ => {
1041 }
1043 }
1044}
1045
1046fn ffi_param_name(name: &str, ty: &TypeRef) -> String {
1047 match ty {
1048 TypeRef::String | TypeRef::Char | TypeRef::Path | TypeRef::Json => "c".to_string() + name,
1049 TypeRef::Named(_) => "c".to_string() + name,
1050 TypeRef::Optional(inner) => match inner.as_ref() {
1051 TypeRef::String | TypeRef::Char | TypeRef::Path | TypeRef::Json | TypeRef::Named(_) => {
1052 "c".to_string() + name
1053 }
1054 _ => name.to_string(),
1055 },
1056 _ => name.to_string(),
1057 }
1058}
1059
1060fn gen_function_descriptor(return_layout: &str, param_layouts: &[String]) -> String {
1063 if return_layout.is_empty() {
1064 if param_layouts.is_empty() {
1066 "FunctionDescriptor.ofVoid()".to_string()
1067 } else {
1068 format!("FunctionDescriptor.ofVoid({})", param_layouts.join(", "))
1069 }
1070 } else {
1071 if param_layouts.is_empty() {
1073 format!("FunctionDescriptor.of({})", return_layout)
1074 } else {
1075 format!("FunctionDescriptor.of({}, {})", return_layout, param_layouts.join(", "))
1076 }
1077 }
1078}
1079
1080fn is_ffi_string_return(ty: &TypeRef) -> bool {
1083 match ty {
1084 TypeRef::String | TypeRef::Char | TypeRef::Path | TypeRef::Json => true,
1085 TypeRef::Optional(inner) => is_ffi_string_return(inner),
1086 _ => false,
1087 }
1088}
1089
1090fn java_ffi_return_cast(ty: &TypeRef) -> &'static str {
1092 match ty {
1093 TypeRef::Primitive(prim) => match prim {
1094 PrimitiveType::Bool => "boolean",
1095 PrimitiveType::U8 | PrimitiveType::I8 => "byte",
1096 PrimitiveType::U16 | PrimitiveType::I16 => "short",
1097 PrimitiveType::U32 | PrimitiveType::I32 => "int",
1098 PrimitiveType::U64 | PrimitiveType::I64 | PrimitiveType::Usize | PrimitiveType::Isize => "long",
1099 PrimitiveType::F32 => "float",
1100 PrimitiveType::F64 => "double",
1101 },
1102 TypeRef::Bytes | TypeRef::Vec(_) | TypeRef::Map(_, _) | TypeRef::Named(_) => "MemorySegment",
1103 _ => "MemorySegment",
1104 }
1105}
1106
1107fn gen_helper_methods(out: &mut String) {
1108 let needs_read_cstring = out.contains("readCString(");
1110 let needs_read_bytes = out.contains("readBytes(");
1111
1112 if !needs_read_cstring && !needs_read_bytes {
1113 return;
1114 }
1115
1116 writeln!(out, " // Helper methods for FFI marshalling").ok();
1117 writeln!(out).ok();
1118
1119 if needs_read_cstring {
1120 writeln!(out, " private static String readCString(MemorySegment ptr) {{").ok();
1121 writeln!(out, " if (ptr == null || ptr.address() == 0) {{").ok();
1122 writeln!(out, " return null;").ok();
1123 writeln!(out, " }}").ok();
1124 writeln!(out, " return ptr.getUtf8String(0);").ok();
1125 writeln!(out, " }}").ok();
1126 writeln!(out).ok();
1127 }
1128
1129 if needs_read_bytes {
1130 writeln!(
1131 out,
1132 " private static byte[] readBytes(MemorySegment ptr, long len) {{"
1133 )
1134 .ok();
1135 writeln!(out, " if (ptr == null || ptr.address() == 0) {{").ok();
1136 writeln!(out, " return new byte[0];").ok();
1137 writeln!(out, " }}").ok();
1138 writeln!(out, " byte[] bytes = new byte[(int) len];").ok();
1139 writeln!(
1140 out,
1141 " MemorySegment.copy(ptr, ValueLayout.JAVA_BYTE.byteSize() * 0, bytes, 0, (int) len);"
1142 )
1143 .ok();
1144 writeln!(out, " return bytes;").ok();
1145 writeln!(out, " }}").ok();
1146 }
1147}
1148
1149fn format_optional_value(ty: &TypeRef, default: &str) -> String {
1156 if default.contains("Optional.") {
1158 return default.to_string();
1159 }
1160
1161 let inner_ty = match ty {
1163 TypeRef::Optional(inner) => inner.as_ref(),
1164 other => other,
1165 };
1166
1167 let formatted_value = match inner_ty {
1169 TypeRef::Primitive(p) => match p {
1170 PrimitiveType::I64 | PrimitiveType::U64 | PrimitiveType::Isize | PrimitiveType::Usize => {
1171 if default.ends_with('L') || default.ends_with('l') {
1173 default.to_string()
1174 } else if default.parse::<i64>().is_ok() {
1175 format!("{}L", default)
1176 } else {
1177 default.to_string()
1178 }
1179 }
1180 PrimitiveType::F32 => {
1181 if default.ends_with('f') || default.ends_with('F') {
1183 default.to_string()
1184 } else if default.parse::<f32>().is_ok() {
1185 format!("{}f", default)
1186 } else {
1187 default.to_string()
1188 }
1189 }
1190 PrimitiveType::F64 => {
1191 default.to_string()
1193 }
1194 _ => default.to_string(),
1195 },
1196 _ => default.to_string(),
1197 };
1198
1199 format!("Optional.of({})", formatted_value)
1200}
1201
1202fn gen_builder_class(package: &str, typ: &TypeDef) -> String {
1203 let mut body = String::with_capacity(2048);
1204
1205 writeln!(body, "public class {}Builder {{", typ.name).ok();
1206 writeln!(body).ok();
1207
1208 for field in &typ.fields {
1210 let field_name = safe_java_field_name(&field.name);
1211
1212 if field.name.starts_with('_') && field.name[1..].chars().all(|c| c.is_ascii_digit())
1214 || field.name.chars().next().is_none_or(|c| c.is_ascii_digit())
1215 {
1216 continue;
1217 }
1218
1219 let field_type = if field.optional {
1222 format!("Optional<{}>", java_boxed_type(&field.ty))
1223 } else if matches!(field.ty, TypeRef::Duration) {
1224 java_boxed_type(&field.ty).to_string()
1225 } else {
1226 java_type(&field.ty).to_string()
1227 };
1228
1229 let default_value = if field.optional {
1230 if let Some(default) = &field.default {
1232 format_optional_value(&field.ty, default)
1234 } else {
1235 "Optional.empty()".to_string()
1237 }
1238 } else {
1239 if let Some(default) = &field.default {
1241 default.clone()
1242 } else {
1243 match &field.ty {
1244 TypeRef::String | TypeRef::Char | TypeRef::Path | TypeRef::Json => "\"\"".to_string(),
1245 TypeRef::Bytes => "new byte[0]".to_string(),
1246 TypeRef::Primitive(p) => match p {
1247 PrimitiveType::Bool => "false".to_string(),
1248 PrimitiveType::F32 | PrimitiveType::F64 => "0.0".to_string(),
1249 _ => "0".to_string(),
1250 },
1251 TypeRef::Vec(_) => "List.of()".to_string(),
1252 TypeRef::Map(_, _) => "Map.of()".to_string(),
1253 TypeRef::Optional(_) => "Optional.empty()".to_string(),
1254 TypeRef::Duration => "null".to_string(),
1255 _ => "null".to_string(),
1256 }
1257 }
1258 };
1259
1260 writeln!(body, " private {} {} = {};", field_type, field_name, default_value).ok();
1261 }
1262
1263 writeln!(body).ok();
1264
1265 for field in &typ.fields {
1267 if field.name.starts_with('_') && field.name[1..].chars().all(|c| c.is_ascii_digit())
1269 || field.name.chars().next().is_none_or(|c| c.is_ascii_digit())
1270 {
1271 continue;
1272 }
1273
1274 let field_name = safe_java_field_name(&field.name);
1275 let field_name_pascal = to_class_name(&field.name);
1276 let field_type = if field.optional {
1277 format!("Optional<{}>", java_boxed_type(&field.ty))
1278 } else if matches!(field.ty, TypeRef::Duration) {
1279 java_boxed_type(&field.ty).to_string()
1280 } else {
1281 java_type(&field.ty).to_string()
1282 };
1283
1284 writeln!(
1285 body,
1286 " public {}Builder with{}({} value) {{",
1287 typ.name, field_name_pascal, field_type
1288 )
1289 .ok();
1290 writeln!(body, " this.{} = value;", field_name).ok();
1291 writeln!(body, " return this;").ok();
1292 writeln!(body, " }}").ok();
1293 writeln!(body).ok();
1294 }
1295
1296 writeln!(body, " public {} build() {{", typ.name).ok();
1298 writeln!(body, " return new {}(", typ.name).ok();
1299 let non_tuple_fields: Vec<_> = typ
1300 .fields
1301 .iter()
1302 .filter(|f| {
1303 !(f.name.starts_with('_') && f.name[1..].chars().all(|c| c.is_ascii_digit())
1305 || f.name.chars().next().is_none_or(|c| c.is_ascii_digit()))
1306 })
1307 .collect();
1308 for (i, field) in non_tuple_fields.iter().enumerate() {
1309 let field_name = safe_java_field_name(&field.name);
1310 let comma = if i < non_tuple_fields.len() - 1 { "," } else { "" };
1311 writeln!(body, " {}{}", field_name, comma).ok();
1312 }
1313 writeln!(body, " );").ok();
1314 writeln!(body, " }}").ok();
1315
1316 writeln!(body, "}}").ok();
1317
1318 let mut out = String::with_capacity(body.len() + 512);
1320
1321 writeln!(out, "// DO NOT EDIT - auto-generated by alef").ok();
1322 writeln!(out, "package {};", package).ok();
1323 writeln!(out).ok();
1324
1325 if body.contains("List<") {
1326 writeln!(out, "import java.util.List;").ok();
1327 }
1328 if body.contains("Map<") {
1329 writeln!(out, "import java.util.Map;").ok();
1330 }
1331 if body.contains("Optional<") {
1332 writeln!(out, "import java.util.Optional;").ok();
1333 }
1334
1335 writeln!(out).ok();
1336 out.push_str(&body);
1337
1338 out
1339}