1use crate::contract::{self, Consumed};
56use crate::corba_idl::java_name;
57use crate::encode::ProbeError;
58use crate::target_keywords::java;
59use serde_json::Value;
60use std::fmt::Write as _;
61use zerodds_ast::{Ast, CharWidth, PrimitiveKind, TypeKind, TypeNode};
62use zerodds_ast_core::Id;
63
64enum Place {
66 Field(String),
68 Modifier {
70 object: String,
72 name: String,
74 },
75 ModifierWithDiscriminator {
78 object: String,
80 name: String,
82 discriminator: String,
84 },
85}
86
87impl Place {
88 fn assign(&self, literal: &str) -> String {
89 match self {
90 Self::Field(expression) => format!("{expression} = {literal};"),
91 Self::Modifier { object, name } => format!("{object}.{name}({literal});"),
92 Self::ModifierWithDiscriminator {
93 object,
94 name,
95 discriminator,
96 } => format!("{object}.{name}({discriminator}, {literal});"),
97 }
98 }
99}
100
101#[derive(Debug, Default)]
104struct Java {
105 statements: String,
106 next: usize,
107 seen: Consumed,
108}
109
110impl Java {
111 fn temporary(&mut self, stem: &str) -> String {
112 self.next += 1;
113 format!("zd_{stem}_{}", self.next)
114 }
115
116 fn line(&mut self, text: &str) {
117 self.statements.push_str(text);
118 self.statements.push('\n');
119 }
120}
121
122pub fn fill_corba_java(
127 ast: &Ast,
128 type_: Id<TypeNode>,
129 values: &Value,
130) -> Result<String, ProbeError> {
131 fill_corba_java_recording(ast, type_, values, &mut Consumed::new())
132}
133
134pub fn fill_corba_java_recording(
140 ast: &Ast,
141 type_: Id<TypeNode>,
142 values: &Value,
143 seen: &mut Consumed,
144) -> Result<String, ProbeError> {
145 contract::reject_alias_cycle(ast)?;
147 let mut out = Java::default();
148 let root = contract::root_path(ast, type_);
149 let result = fill_at(
150 ast,
151 type_,
152 values,
153 &Place::Field(String::from("sample")),
154 &mut out,
155 &root,
156 );
157 *seen = core::mem::take(&mut out.seen);
158 result?;
159 Ok(out.statements)
160}
161
162fn java_type(ast: &Ast, type_: Id<TypeNode>, path: &str) -> Result<String, ProbeError> {
175 let node = ast.type_(type_).map_err(|error| ProbeError::NoLayout {
176 detail: format!("{error}"),
177 })?;
178 Ok(match &node.kind {
179 TypeKind::Primitive(kind) => java_primitive(*kind, path)?.to_string(),
180 TypeKind::String { .. } => "java.lang.String".to_string(),
181 TypeKind::Fixed { .. } => "java.math.BigDecimal".to_string(),
182 TypeKind::Sequence { element, .. } | TypeKind::Array { element, .. } => {
183 let element = element.resolved().ok_or_else(|| ProbeError::NoLayout {
184 detail: format!("{path}: the element type is unresolved"),
185 })?;
186 format!("{}[]", java_type(ast, element, path)?)
187 }
188 TypeKind::Alias { aliased } => {
189 let target = aliased.resolved().ok_or_else(|| ProbeError::NoLayout {
190 detail: format!("{path}: the typedef target is unresolved"),
191 })?;
192 java_type(ast, target, path)?
193 }
194 TypeKind::Structure { .. }
195 | TypeKind::Union { .. }
196 | TypeKind::Enumeration { .. }
197 | TypeKind::Valuetype { .. } => java_name(ast, type_),
198 TypeKind::Map { .. } => {
203 return Err(ProbeError::Unsupported {
204 path: path.to_string(),
205 kind: "a map, which is IDL 4.2 §7.4.13.4.3 and outside JacORB 3.9's grammar — its idl says `Syntax error` at the keyword, measured on the test host",
206 });
207 }
208 other => {
209 return Err(ProbeError::Unfilled {
210 path: path.to_string(),
211 kind: other.as_str(),
212 });
213 }
214 })
215}
216
217#[allow(clippy::too_many_lines)]
226fn fill_at(
227 ast: &Ast,
228 type_: Id<TypeNode>,
229 value: &Value,
230 place: &Place,
231 out: &mut Java,
232 path: &str,
233) -> Result<(), ProbeError> {
234 let node = ast.type_(type_).map_err(|error| ProbeError::NoLayout {
235 detail: format!("{error}"),
236 })?;
237 match &node.kind {
238 TypeKind::Primitive(kind) => {
239 let literal = primitive_literal(*kind, value, path)?;
240 out.seen.scalar(path);
241 let statement = place.assign(&literal);
242 out.line(&statement);
243 Ok(())
244 }
245 TypeKind::String { .. } => {
250 let text = contract::text(value, path)?;
251 out.seen.scalar(path);
252 let statement = place.assign(&java_string(text));
253 out.line(&statement);
254 Ok(())
255 }
256 TypeKind::Fixed { .. } => {
257 let text = contract::text(value, path)?;
258 out.seen.scalar(path);
259 let statement =
260 place.assign(&format!("new java.math.BigDecimal({})", java_string(text)));
261 out.line(&statement);
262 Ok(())
263 }
264 TypeKind::Enumeration { .. } => {
265 let (name, _) = contract::enumerator(ast, type_, value, path)?;
266 out.seen.scalar(path);
267 let statement = place.assign(&format!("{}.{}", java_name(ast, type_), java(&name)));
271 out.line(&statement);
272 Ok(())
273 }
274 TypeKind::Alias { aliased } => {
275 let target = aliased.resolved().ok_or_else(|| ProbeError::NoLayout {
276 detail: format!("{path}: the typedef target is unresolved"),
277 })?;
278 fill_at(ast, target, value, place, out, path)
279 }
280 TypeKind::Structure { .. } | TypeKind::Valuetype { .. } => {
281 let object = contract::object(value, path)?;
282 let members = contract::members(ast, type_);
283 let declared: Vec<String> = members.iter().map(|member| member.name.clone()).collect();
284 contract::reject_unknown_keys(object, &declared, path)?;
285 let class = java_type(ast, type_, path)?;
290 let local = out.temporary("obj");
291 out.line(&format!("{class} {local} = new {class}();"));
292 for member in &members {
293 let Some(member_value) = contract::member_value(object, member, path)? else {
294 continue;
295 };
296 fill_at(
297 ast,
298 member.type_,
299 member_value,
300 &Place::Field(format!("{local}.{}", java(&member.name))),
303 out,
304 &contract::join(path, &member.name),
305 )?;
306 }
307 let statement = place.assign(&local);
308 out.line(&statement);
309 Ok(())
310 }
311 TypeKind::Union { discriminator, .. } => {
312 let object = contract::object(value, path)?;
313 let discriminant = contract::discriminator(object, path)?;
314 let class = java_type(ast, type_, path)?;
315 let local = out.temporary("union");
316 out.line(&format!("{class} {local} = new {class}();"));
317 let discriminator = discriminator
318 .resolved()
319 .ok_or_else(|| ProbeError::NoLayout {
320 detail: format!("{path}: the discriminator is unresolved"),
321 })?;
322 let disc_literal = discriminator_literal(ast, discriminator, discriminant, path)?;
323
324 let mut selected = false;
325 for (name, member_value) in object {
326 if name == "_d" {
327 continue;
328 }
329 let Some(case) = union_case(ast, type_, name) else {
330 return Err(ProbeError::Unknown {
331 path: contract::join(path, name),
332 declared: union_member_names(ast, type_),
333 });
334 };
335 contract::reject_null(member_value, &contract::join(path, name))?;
336 selected = true;
337 let member_place = if case.explicit_labels == 1 && !case.is_default {
347 Place::Modifier {
348 object: local.clone(),
349 name: java(name).into_owned(),
350 }
351 } else {
352 Place::ModifierWithDiscriminator {
353 object: local.clone(),
354 name: java(name).into_owned(),
355 discriminator: disc_literal.clone(),
356 }
357 };
358 fill_at(
359 ast,
360 case.type_,
361 member_value,
362 &member_place,
363 out,
364 &contract::join(path, name),
365 )?;
366 }
367 if !selected {
368 out.line(&format!("{local}.__default({disc_literal});"));
373 }
374 let statement = place.assign(&local);
375 out.line(&statement);
376 Ok(())
377 }
378 TypeKind::Array { element, dims } => {
379 let element = element.resolved().ok_or_else(|| ProbeError::NoLayout {
380 detail: format!("{path}: the array element is unresolved"),
381 })?;
382 let counts = contract::array_dimensions(ast, dims, path)?;
383 let local = out.temporary("arr");
384 let brackets: String = counts.iter().map(|count| format!("[{count}]")).collect();
385 let (declared, allocation) =
386 java_array(&java_type(ast, element, path)?, counts.len(), &brackets);
387 out.line(&format!("{declared} {local} = {allocation};"));
388 fill_array(ast, element, value, &local, out, path, &counts)?;
389 let statement = place.assign(&local);
390 out.line(&statement);
391 Ok(())
392 }
393 TypeKind::Sequence { element, bound } => {
394 let element = element.resolved().ok_or_else(|| ProbeError::NoLayout {
395 detail: format!("{path}: the sequence element is unresolved"),
396 })?;
397 let items = contract::bounded_items(ast, value, *bound, "elements", path)?;
398 let local = out.temporary("seq");
399 let (declared, allocation) = java_array(
400 &java_type(ast, element, path)?,
401 1,
402 &format!("[{}]", items.len()),
403 );
404 out.line(&format!("{declared} {local} = {allocation};"));
405 for (index, item) in items.iter().enumerate() {
406 fill_at(
407 ast,
408 element,
409 item,
410 &Place::Field(format!("{local}[{index}]")),
411 out,
412 &format!("{path}[{index}]"),
413 )?;
414 }
415 let statement = place.assign(&local);
416 out.line(&statement);
417 Ok(())
418 }
419 TypeKind::Bitmask { .. } => Err(ProbeError::Unsupported {
420 path: path.to_string(),
421 kind: "a bitmask, which is XTypes 1.3 §7.3.1.2.1.4 and not CORBA IDL",
422 }),
423 TypeKind::Bitset { .. } => Err(ProbeError::Unsupported {
424 path: path.to_string(),
425 kind: "a bitset, which is XTypes 1.3 §7.3.1.2.1.3 and not CORBA IDL",
426 }),
427 TypeKind::Map { .. } => Err(ProbeError::Unsupported {
428 path: path.to_string(),
429 kind: "a map, which is IDL 4.2 §7.4.13.4.3 and outside JacORB 3.9's grammar — its idl says `Syntax error` at the keyword, measured on the test host",
430 }),
431 other => Err(ProbeError::Unfilled {
432 path: path.to_string(),
433 kind: other.as_str(),
434 }),
435 }
436}
437
438#[allow(clippy::too_many_arguments)]
442fn fill_array(
443 ast: &Ast,
444 element: Id<TypeNode>,
445 value: &Value,
446 lvalue: &str,
447 out: &mut Java,
448 path: &str,
449 counts: &[usize],
450) -> Result<(), ProbeError> {
451 let Some((&count, rest)) = counts.split_first() else {
452 return fill_at(
453 ast,
454 element,
455 value,
456 &Place::Field(lvalue.to_string()),
457 out,
458 path,
459 );
460 };
461 let items = contract::array(value, path)?;
462 if items.len() != count {
463 return Err(ProbeError::Mismatch {
464 path: path.to_string(),
465 expected: format!("exactly {count} elements"),
466 found: format!("{}", items.len()),
467 });
468 }
469 for (index, item) in items.iter().enumerate() {
470 fill_array(
471 ast,
472 element,
473 item,
474 &format!("{lvalue}[{index}]"),
475 out,
476 &format!("{path}[{index}]"),
477 rest,
478 )?;
479 }
480 Ok(())
481}
482
483#[derive(Debug, Default)]
487struct Dump {
488 body: String,
489 depth: usize,
490 next: usize,
491 rec: Vec<Id<TypeNode>>,
495}
496
497impl Dump {
498 fn line(&mut self, text: &str) {
499 for _ in 0..self.depth {
500 self.body.push_str(" ");
501 }
502 self.body.push_str(text);
503 self.body.push('\n');
504 }
505
506 fn open(&mut self, text: &str) {
507 self.line(text);
508 self.depth += 1;
509 }
510
511 fn close(&mut self, text: &str) {
512 self.depth = self.depth.saturating_sub(1);
513 self.line(text);
514 }
515
516 fn raw(&mut self, literal: &str) {
517 self.line(&format!("Zdj.raw({});", java_string(literal)));
518 }
519
520 fn temporary(&mut self, stem: &str) -> String {
521 self.next += 1;
522 format!("zd_{stem}_{}", self.next)
523 }
524}
525
526pub fn dump_corba_java(ast: &Ast, type_: Id<TypeNode>) -> Result<String, ProbeError> {
531 contract::reject_alias_cycle(ast)?;
533 let mut out = Dump {
534 rec: crate::recursion::recursive_aggregates(ast, type_),
535 ..Dump::default()
536 };
537 dump_at(ast, type_, "sample", &mut out, "")?;
538 out.line("Zdj.raw(\"\\n\");");
539 Ok(out.body)
540}
541
542#[allow(clippy::too_many_lines)]
560fn dump_at(
561 ast: &Ast,
562 type_: Id<TypeNode>,
563 expr: &str,
564 out: &mut Dump,
565 path: &str,
566) -> Result<(), ProbeError> {
567 let node = ast.type_(type_).map_err(|error| ProbeError::NoLayout {
568 detail: format!("{error}"),
569 })?;
570 match &node.kind {
571 TypeKind::Primitive(kind) => {
572 out.line(&primitive_print(*kind, expr, path)?);
573 Ok(())
574 }
575 TypeKind::String {
576 width: CharWidth::Char,
577 ..
578 } => {
579 out.line(&format!("Zdj.string({expr});"));
580 Ok(())
581 }
582 TypeKind::String {
583 width: CharWidth::WChar,
584 ..
585 } => {
586 out.line(&format!("Zdj.wstring({expr});"));
587 Ok(())
588 }
589 TypeKind::Fixed { .. } => {
590 out.line(&format!("Zdj.fixed({expr});"));
591 Ok(())
592 }
593 TypeKind::Enumeration { .. } => {
594 dump_enumeration(ast, type_, expr, out);
595 Ok(())
596 }
597 TypeKind::Alias { aliased } => {
598 let target = aliased.resolved().ok_or_else(|| ProbeError::NoLayout {
599 detail: format!("{path}: the typedef target is unresolved"),
600 })?;
601 dump_at(ast, target, expr, out, path)
602 }
603 TypeKind::Structure { .. } | TypeKind::Valuetype { .. } => {
604 if out.rec.contains(&type_) {
605 return Err(recursive_type(path));
606 }
607 let first = out.temporary("first");
608 out.open("{");
609 out.line(&format!("boolean[] {first} = {{ true }};"));
610 out.raw("{");
611 for member in contract::members(ast, type_) {
612 out.line(&format!("Zdj.key({first}, \"{}\");", member.name));
614 dump_at(
615 ast,
616 member.type_,
617 &format!("{expr}.{}", java(&member.name)),
618 out,
619 &contract::join(path, &member.name),
620 )?;
621 }
622 out.raw("}");
623 out.close("}");
624 Ok(())
625 }
626 TypeKind::Union { discriminator, .. } => {
627 if out.rec.contains(&type_) {
628 return Err(recursive_type(path));
629 }
630 let discriminator = discriminator
631 .resolved()
632 .ok_or_else(|| ProbeError::NoLayout {
633 detail: format!("{path}: the discriminator is unresolved"),
634 })?;
635 let first = out.temporary("first");
636 out.open("{");
637 out.line(&format!("boolean[] {first} = {{ true }};"));
638 out.raw("{");
639 out.line(&format!("Zdj.key({first}, \"_d\");"));
640 dump_at(
641 ast,
642 discriminator,
643 &format!("{expr}.discriminator()"),
644 out,
645 &format!("{path}._d"),
646 )?;
647 let resolved = contract::resolve_alias(ast, discriminator);
658 let selector = match ast.type_(resolved).map(|node| node.kind.clone()) {
659 Ok(TypeKind::Enumeration { .. }) => {
660 format!("{expr}.discriminator().value()")
661 }
662 Ok(TypeKind::Primitive(PrimitiveKind::Boolean)) => {
663 format!("{expr}.discriminator() ? 1 : 0")
664 }
665 _ => format!("(int)({expr}.discriminator())"),
666 };
667 out.open(&format!("switch ({selector}) {{"));
668 for id in ast.cases_of(type_) {
669 let Ok(case) = ast.case(*id) else { continue };
670 let Ok(member) = ast.member(case.member) else {
671 continue;
672 };
673 let Some(member_type) = member.type_.resolved() else {
674 continue;
675 };
676 for label in &case.labels {
677 match label.expr() {
678 Some(expression) => match ast.eval_integral(expression) {
679 Ok(value) => out.line(&format!("case {}:", value as i32)),
680 Err(error) => {
681 return Err(ProbeError::NoLayout {
682 detail: format!(
683 "{path}: a case label does not evaluate: {error}"
684 ),
685 });
686 }
687 },
688 None => out.line("default:"),
689 }
690 }
691 out.depth += 1;
692 out.line(&format!("Zdj.key({first}, \"{}\");", member.name));
693 dump_at(
694 ast,
695 member_type,
696 &format!("{expr}.{}()", java(&member.name)),
697 out,
698 &contract::join(path, &member.name),
699 )?;
700 out.line("break;");
701 out.depth = out.depth.saturating_sub(1);
702 }
703 out.close("}");
704 out.raw("}");
705 out.close("}");
706 Ok(())
707 }
708 TypeKind::Array { element, dims } => {
709 let element = element.resolved().ok_or_else(|| ProbeError::NoLayout {
710 detail: format!("{path}: the array element is unresolved"),
711 })?;
712 let counts = contract::array_dimensions(ast, dims, path)?;
713 dump_array(ast, element, expr, out, path, &counts)
714 }
715 TypeKind::Sequence { element, .. } => {
716 let element = element.resolved().ok_or_else(|| ProbeError::NoLayout {
717 detail: format!("{path}: the sequence element is unresolved"),
718 })?;
719 let index = out.temporary("i");
720 out.open("{");
721 out.raw("[");
722 out.open(&format!(
723 "for (int {index} = 0; {index} < {expr}.length; {index}++) {{"
724 ));
725 out.line(&format!("if ({index} > 0) {{ Zdj.raw(\",\"); }}"));
726 dump_at(
727 ast,
728 element,
729 &format!("{expr}[{index}]"),
730 out,
731 &format!("{path}[]"),
732 )?;
733 out.close("}");
734 out.raw("]");
735 out.close("}");
736 Ok(())
737 }
738 TypeKind::Bitmask { .. } => Err(ProbeError::Unsupported {
739 path: path.to_string(),
740 kind: "a bitmask, which is XTypes 1.3 §7.3.1.2.1.4 and not CORBA IDL",
741 }),
742 TypeKind::Bitset { .. } => Err(ProbeError::Unsupported {
743 path: path.to_string(),
744 kind: "a bitset, which is XTypes 1.3 §7.3.1.2.1.3 and not CORBA IDL",
745 }),
746 TypeKind::Map { .. } => Err(ProbeError::Unsupported {
747 path: path.to_string(),
748 kind: "a map, which is IDL 4.2 §7.4.13.4.3 and outside JacORB 3.9's grammar — its idl says `Syntax error` at the keyword, measured on the test host",
749 }),
750 other => Err(ProbeError::Unfilled {
751 path: path.to_string(),
752 kind: other.as_str(),
753 }),
754 }
755}
756
757fn recursive_type(path: &str) -> ProbeError {
760 ProbeError::Unsupported {
761 path: path.to_string(),
762 kind: "a recursive type, which CORBA CDR frames its own way — the same refusal \
763 `corba_idl` makes, said here because the expansion of a cycle does not end",
764 }
765}
766
767fn dump_array(
772 ast: &Ast,
773 element: Id<TypeNode>,
774 expr: &str,
775 out: &mut Dump,
776 path: &str,
777 counts: &[usize],
778) -> Result<(), ProbeError> {
779 let Some((&count, rest)) = counts.split_first() else {
780 return dump_at(ast, element, expr, out, path);
781 };
782 out.raw("[");
783 for index in 0..count {
784 if index > 0 {
785 out.raw(",");
786 }
787 dump_array(
788 ast,
789 element,
790 &format!("{expr}[{index}]"),
791 out,
792 &format!("{path}[{index}]"),
793 rest,
794 )?;
795 }
796 out.raw("]");
797 Ok(())
798}
799
800fn dump_enumeration(ast: &Ast, type_: Id<TypeNode>, expr: &str, out: &mut Dump) {
813 let class = java_name(ast, type_);
814 out.open(&format!("switch ({expr}.value()) {{"));
815 for id in ast.enumerators_of(type_) {
816 let Ok(node) = ast.enumerator(*id) else {
817 continue;
818 };
819 out.line(&format!("case {class}._{}:", java(&node.name)));
820 out.line(&format!(" Zdj.raw(\"\\\"{}\\\"\"); break;", node.name));
821 }
822 out.line(&format!("default: Zdj.enumUnknown({expr}.value()); break;"));
823 out.close("}");
824}
825
826fn java_array(element: &str, depth: usize, sizes: &str) -> (String, String) {
838 let base = element.trim_end_matches("[]");
839 let suffix = &element[base.len()..];
840 let declared = format!("{base}{}{suffix}", "[]".repeat(depth));
841 (declared, format!("new {base}{sizes}{suffix}"))
842}
843
844fn java_primitive(kind: PrimitiveKind, path: &str) -> Result<&'static str, ProbeError> {
846 Ok(match kind {
847 PrimitiveKind::Boolean => "boolean",
848 PrimitiveKind::Octet | PrimitiveKind::Int8 | PrimitiveKind::UInt8 => "byte",
851 PrimitiveKind::Char | PrimitiveKind::WChar => "char",
852 PrimitiveKind::Int16 | PrimitiveKind::UInt16 => "short",
853 PrimitiveKind::Int32 | PrimitiveKind::UInt32 => "int",
854 PrimitiveKind::Int64 | PrimitiveKind::UInt64 => "long",
855 PrimitiveKind::Float32 => "float",
856 PrimitiveKind::Float64 => "double",
857 PrimitiveKind::Float128 => {
858 return Err(ProbeError::Unsupported {
859 path: path.to_string(),
860 kind: "a long double, which the IDL-to-Java mapping §1.5.2 has no type for",
861 });
862 }
863 })
864}
865
866fn primitive_literal(kind: PrimitiveKind, value: &Value, path: &str) -> Result<String, ProbeError> {
873 Ok(match kind {
874 PrimitiveKind::Boolean => contract::boolean(value, path)?.to_string(),
875 PrimitiveKind::Char | PrimitiveKind::WChar => {
876 let code = contract::character_for(kind, value, path)?;
877 format!("(char)0x{:04x}", code as u32)
878 }
879 PrimitiveKind::Octet | PrimitiveKind::Int8 | PrimitiveKind::UInt8 => {
880 let integer = contract::integer_for(kind, value, path)?;
881 #[allow(clippy::cast_possible_truncation)]
882 let octet = (integer as i64) as u8;
883 format!("(byte)0x{octet:02x}")
884 }
885 PrimitiveKind::Int16 | PrimitiveKind::UInt16 => {
886 let integer = contract::integer_for(kind, value, path)?;
887 #[allow(clippy::cast_possible_truncation)]
888 let bits = (integer as i64) as u16;
889 format!("(short)0x{bits:04x}")
890 }
891 PrimitiveKind::Int32 | PrimitiveKind::UInt32 => {
892 let integer = contract::integer_for(kind, value, path)?;
893 #[allow(clippy::cast_possible_truncation)]
894 let bits = (integer as i64) as u32;
895 format!("0x{bits:08x}")
896 }
897 PrimitiveKind::Int64 | PrimitiveKind::UInt64 => {
898 let integer = contract::integer_for(kind, value, path)?;
899 #[allow(clippy::cast_possible_truncation)]
900 let bits = integer as u64;
901 format!("0x{bits:016x}L")
902 }
903 PrimitiveKind::Float32 => {
908 #[allow(clippy::cast_possible_truncation)]
909 let bits = (contract::float(value, path)? as f32).to_bits();
910 format!("Float.intBitsToFloat(0x{bits:08x})")
911 }
912 PrimitiveKind::Float64 => {
913 let bits = contract::float(value, path)?.to_bits();
914 format!("Double.longBitsToDouble(0x{bits:016x}L)")
915 }
916 PrimitiveKind::Float128 => {
917 return Err(ProbeError::Unsupported {
918 path: path.to_string(),
919 kind: "a long double, which the IDL-to-Java mapping §1.5.2 has no type for",
920 });
921 }
922 })
923}
924
925fn discriminator_literal(
927 ast: &Ast,
928 discriminator: Id<TypeNode>,
929 value: &Value,
930 path: &str,
931) -> Result<String, ProbeError> {
932 let resolved = contract::resolve_alias(ast, discriminator);
933 let node = ast.type_(resolved).map_err(|error| ProbeError::NoLayout {
934 detail: format!("{error}"),
935 })?;
936 match &node.kind {
937 TypeKind::Enumeration { .. } => {
938 let (name, _) = contract::enumerator(ast, resolved, value, path)?;
939 Ok(format!("{}.{}", java_name(ast, resolved), java(&name)))
940 }
941 TypeKind::Primitive(kind) => primitive_literal(*kind, value, path),
942 other => Err(ProbeError::Unfilled {
943 path: path.to_string(),
944 kind: other.as_str(),
945 }),
946 }
947}
948
949fn primitive_print(kind: PrimitiveKind, expr: &str, path: &str) -> Result<String, ProbeError> {
956 Ok(match kind {
957 PrimitiveKind::Boolean => format!("Zdj.bool({expr});"),
958 PrimitiveKind::Char => format!("Zdj.ch({expr});"),
959 PrimitiveKind::WChar => format!("Zdj.wch({expr});"),
960 PrimitiveKind::Int8 => format!("Zdj.i64((long)({expr}));"),
961 PrimitiveKind::Octet | PrimitiveKind::UInt8 => {
962 format!("Zdj.u64((long)({expr}) & 0xFFL);")
963 }
964 PrimitiveKind::Int16 => format!("Zdj.i64((long)({expr}));"),
965 PrimitiveKind::UInt16 => format!("Zdj.u64((long)({expr}) & 0xFFFFL);"),
966 PrimitiveKind::Int32 => format!("Zdj.i64((long)({expr}));"),
967 PrimitiveKind::UInt32 => format!("Zdj.u64((long)({expr}) & 0xFFFFFFFFL);"),
968 PrimitiveKind::Int64 => format!("Zdj.i64String({expr});"),
969 PrimitiveKind::UInt64 => format!("Zdj.u64String({expr});"),
972 PrimitiveKind::Float32 => format!("Zdj.f32({expr});"),
973 PrimitiveKind::Float64 => format!("Zdj.f64({expr});"),
974 PrimitiveKind::Float128 => {
975 return Err(ProbeError::Unsupported {
976 path: path.to_string(),
977 kind: "a long double, which the IDL-to-Java mapping §1.5.2 has no type for",
978 });
979 }
980 })
981}
982
983struct UnionCase {
986 type_: Id<TypeNode>,
988 explicit_labels: usize,
990 is_default: bool,
992}
993
994fn union_case(ast: &Ast, union: Id<TypeNode>, name: &str) -> Option<UnionCase> {
995 for id in ast.cases_of(union) {
996 let case = ast.case(*id).ok()?;
997 let member = ast.member(case.member).ok()?;
998 if member.name != name {
999 continue;
1000 }
1001 return Some(UnionCase {
1002 type_: member.type_.resolved()?,
1003 explicit_labels: case
1004 .labels
1005 .iter()
1006 .filter(|label| label.expr().is_some())
1007 .count(),
1008 is_default: case.labels.iter().any(|label| label.expr().is_none()),
1009 });
1010 }
1011 None
1012}
1013
1014fn union_member_names(ast: &Ast, union: Id<TypeNode>) -> Vec<String> {
1015 contract::union_cases(ast, union)
1016 .into_iter()
1017 .map(|case| case.name)
1018 .collect()
1019}
1020
1021fn java_string(text: &str) -> String {
1027 let mut out = String::from("\"");
1028 for ch in text.chars() {
1029 match ch {
1030 '"' => out.push_str("\\\""),
1031 '\\' => out.push_str("\\\\"),
1032 '\n' => out.push_str("\\n"),
1033 '\r' => out.push_str("\\r"),
1034 '\t' => out.push_str("\\t"),
1035 other if other.is_ascii_graphic() || other == ' ' => out.push(other),
1036 other => {
1037 let mut buffer = [0u16; 2];
1038 for unit in other.encode_utf16(&mut buffer) {
1039 let _ = write!(out, "\\u{unit:04x}");
1040 }
1041 }
1042 }
1043 }
1044 out.push('"');
1045 out
1046}