1use crate::error::TypeDescriptorErr;
2use core::fmt;
3use num_enum::TryFromPrimitive;
4use std::fmt::{Display, Formatter};
5use std::iter::Peekable;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub enum TypeArg {
9 Any, Extends(Box<JavaType>), Super(Box<JavaType>), Exact(Box<JavaType>), }
14
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub struct ClassSignatureSegment {
17 pub name: String,
18 pub args: Vec<TypeArg>,
19}
20
21#[derive(Debug, Clone, PartialEq, Eq)]
22pub struct ClassSignature {
23 pub first: ClassSignatureSegment,
24 pub suffix: Vec<ClassSignatureSegment>,
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub enum PrimitiveType {
29 Byte,
30 Char,
31 Double,
32 Float,
33 Int,
34 Long,
35 Short,
36 Boolean,
37}
38
39impl PrimitiveType {
40 pub fn values() -> &'static [PrimitiveType] {
41 static PRIMITIVE_TYPES: [PrimitiveType; 8] = [
42 PrimitiveType::Byte,
43 PrimitiveType::Char,
44 PrimitiveType::Double,
45 PrimitiveType::Float,
46 PrimitiveType::Int,
47 PrimitiveType::Long,
48 PrimitiveType::Short,
49 PrimitiveType::Boolean,
50 ];
51 &PRIMITIVE_TYPES
52 }
53}
54
55impl Display for PrimitiveType {
56 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
57 match self {
58 PrimitiveType::Byte => write!(f, "byte"),
59 PrimitiveType::Char => write!(f, "char"),
60 PrimitiveType::Double => write!(f, "double"),
61 PrimitiveType::Float => write!(f, "float"),
62 PrimitiveType::Int => write!(f, "int"),
63 PrimitiveType::Long => write!(f, "long"),
64 PrimitiveType::Short => write!(f, "short"),
65 PrimitiveType::Boolean => write!(f, "boolean"),
66 }
67 }
68}
69
70impl TryFrom<char> for PrimitiveType {
71 type Error = (); fn try_from(value: char) -> Result<Self, Self::Error> {
74 match value {
75 'B' => Ok(PrimitiveType::Byte),
76 'C' => Ok(PrimitiveType::Char),
77 'D' => Ok(PrimitiveType::Double),
78 'F' => Ok(PrimitiveType::Float),
79 'I' => Ok(PrimitiveType::Int),
80 'J' => Ok(PrimitiveType::Long),
81 'S' => Ok(PrimitiveType::Short),
82 'Z' => Ok(PrimitiveType::Boolean),
83 _ => Err(()),
84 }
85 }
86}
87
88#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive)]
89#[repr(u8)]
90pub enum AllocationType {
91 Boolean = 0,
92 Byte = 1,
93 Char = 2,
94 Short = 3,
95 Int = 4,
96 Long = 5,
97 Float = 6,
98 Double = 7,
99 Reference = 8,
100}
101
102impl AllocationType {
103 pub fn byte_size(&self) -> usize {
104 match self {
105 AllocationType::Byte | AllocationType::Boolean => 1,
106 AllocationType::Char | AllocationType::Short => 2,
107 AllocationType::Int | AllocationType::Float => 4,
108 AllocationType::Long | AllocationType::Double => 8,
109 AllocationType::Reference => 8, }
111 }
112}
113
114#[derive(Debug, Clone, PartialEq, Eq)]
117pub enum JavaType {
118 Primitive(PrimitiveType),
119 Instance(String), GenericInstance(ClassSignature),
121 TypeVar(String),
122 Array(Box<JavaType>),
123}
124
125#[derive(Debug, Clone, PartialEq, Eq)]
127pub enum ReturnType {
128 Void,
129 Type(JavaType),
130}
131
132impl TryFrom<&str> for JavaType {
133 type Error = TypeDescriptorErr;
134
135 fn try_from(value: &str) -> Result<Self, Self::Error> {
136 JavaType::try_recursive(&mut value.chars().peekable())
137 }
138}
139
140impl JavaType {
141 pub fn as_allocation_type(&self) -> AllocationType {
142 match self {
143 JavaType::Primitive(prim) => match prim {
144 PrimitiveType::Byte => AllocationType::Byte,
145 PrimitiveType::Char => AllocationType::Char,
146 PrimitiveType::Double => AllocationType::Double,
147 PrimitiveType::Float => AllocationType::Float,
148 PrimitiveType::Int => AllocationType::Int,
149 PrimitiveType::Long => AllocationType::Long,
150 PrimitiveType::Short => AllocationType::Short,
151 PrimitiveType::Boolean => AllocationType::Boolean,
152 },
153 JavaType::Instance(_) | JavaType::Array(_) => AllocationType::Reference,
154 _ => panic!("Cannot get allocation type for non-primitive, non-instance JavaType"),
155 }
156 }
157
158 pub fn get_array_dimension(&self) -> usize {
160 match self {
161 JavaType::Array(elem) => 1 + elem.get_array_dimension(),
162 _ => 0,
163 }
164 }
165
166 pub fn as_descriptor(&self) -> String {
168 match self {
169 JavaType::Primitive(prim) => match prim {
170 PrimitiveType::Byte => "B".to_string(),
171 PrimitiveType::Char => "C".to_string(),
172 PrimitiveType::Double => "D".to_string(),
173 PrimitiveType::Float => "F".to_string(),
174 PrimitiveType::Int => "I".to_string(),
175 PrimitiveType::Long => "J".to_string(),
176 PrimitiveType::Short => "S".to_string(),
177 PrimitiveType::Boolean => "Z".to_string(),
178 },
179 JavaType::Instance(name) => format!("L{};", name),
180 JavaType::GenericInstance(sig) => {
181 let mut desc = format!("L{}", sig.first.name);
182 if !sig.first.args.is_empty() {
183 desc.push('<');
184 for arg in &sig.first.args {
185 match arg {
186 TypeArg::Any => desc.push('*'),
187 TypeArg::Extends(t) => {
188 desc.push('+');
189 desc.push_str(&t.as_descriptor());
190 }
191 TypeArg::Super(t) => {
192 desc.push('-');
193 desc.push_str(&t.as_descriptor());
194 }
195 TypeArg::Exact(t) => {
196 desc.push_str(&t.as_descriptor());
197 }
198 }
199 }
200 desc.push('>');
201 }
202 for seg in &sig.suffix {
203 desc.push('.');
204 desc.push_str(&seg.name);
205 if !seg.args.is_empty() {
206 desc.push('<');
207 for arg in &seg.args {
208 match arg {
209 TypeArg::Any => desc.push('*'),
210 TypeArg::Extends(t) => {
211 desc.push('+');
212 desc.push_str(&t.as_descriptor());
213 }
214 TypeArg::Super(t) => {
215 desc.push('-');
216 desc.push_str(&t.as_descriptor());
217 }
218 TypeArg::Exact(t) => {
219 desc.push_str(&t.as_descriptor());
220 }
221 }
222 }
223 desc.push('>');
224 }
225 }
226 desc.push(';');
227 desc
228 }
229 JavaType::TypeVar(name) => format!("T{};", name),
230 JavaType::Array(elem) => format!("[{}", elem.as_descriptor()),
231 }
232 }
233
234 pub fn get_array_element_type(&self) -> Option<&JavaType> {
235 match self {
236 JavaType::Array(elem) => match elem.as_ref() {
237 JavaType::Primitive(_) | JavaType::Instance(_) | JavaType::Array(_) => Some(elem),
238 _ => None,
239 },
240 _ => None,
241 }
242 }
243
244 pub fn is_primitive_array(&self) -> bool {
245 match self {
246 JavaType::Array(elem) => matches!(**elem, JavaType::Primitive(_)),
247 _ => false,
248 }
249 }
250
251 pub fn try_recursive<I>(it: &mut Peekable<I>) -> Result<JavaType, TypeDescriptorErr>
254 where
255 I: Iterator<Item = char>,
256 {
257 let c = it.peek().ok_or(TypeDescriptorErr::UnexpectedEnd)?;
258
259 if *c == 'V' {
261 return Err(TypeDescriptorErr::InvalidType('V'));
262 }
263
264 if let Ok(prim) = PrimitiveType::try_from(*c) {
266 it.next();
267 return Ok(JavaType::Primitive(prim));
268 }
269
270 match c {
271 '[' => {
272 it.next();
273 let elem = JavaType::try_recursive(it)?;
274 Ok(JavaType::Array(Box::new(elem)))
275 }
276 'L' => {
277 it.next();
278 let mut name = String::new();
279 let mut has_type_args = false;
280 let mut has_dot = false;
281 let mut depth = 0;
282
283 loop {
284 let ch = it.next().ok_or(TypeDescriptorErr::UnexpectedEnd)?;
285 match ch {
286 '<' => {
287 has_type_args = true;
288 depth += 1;
289 name.push(ch);
290 }
291 '>' => {
292 depth -= 1;
293 name.push(ch);
294 if depth == 0 {
295 match it.peek() {
297 Some('.') | Some(';') => {}
298 Some(&c) => return Err(TypeDescriptorErr::InvalidType(c)),
299 None => return Err(TypeDescriptorErr::UnexpectedEnd),
300 }
301 }
302 }
303 '.' => {
304 has_dot = true;
305 name.push(ch);
306 }
307 ';' => {
308 if depth == 0 {
309 break;
310 }
311 name.push(ch);
312 }
313 _ => {
314 name.push(ch);
315 }
316 }
317 }
318
319 if has_type_args || has_dot {
320 let sig = Self::parse_class_signature(&name)?;
321 Ok(JavaType::GenericInstance(sig))
322 } else {
323 Ok(JavaType::Instance(name))
324 }
325 }
326 'T' => {
327 it.next();
328 let mut name = String::new();
329 loop {
330 let ch = it.next().ok_or(TypeDescriptorErr::UnexpectedEnd)?;
331 if ch == ';' {
332 break;
333 }
334 name.push(ch);
335 }
336 Ok(JavaType::TypeVar(name))
337 }
338 _ => Err(TypeDescriptorErr::InvalidType(*c)),
339 }
340 }
341
342 fn parse_class_signature(s: &str) -> Result<ClassSignature, TypeDescriptorErr> {
343 let mut it = s.chars().peekable();
344 let mut segments = Vec::new();
345
346 loop {
347 let mut seg_name = String::new();
348
349 while let Some(&ch) = it.peek() {
351 if ch == '<' || ch == '.' {
352 break;
353 }
354 seg_name.push(ch);
355 it.next();
356 }
357
358 let mut args = Vec::new();
359 if it.peek() == Some(&'<') {
360 it.next(); loop {
362 match it.peek() {
363 Some('>') => {
364 it.next(); break;
366 }
367 Some('*') => {
368 it.next();
369 args.push(TypeArg::Any);
370 }
371 Some('+') => {
372 it.next();
373 let ty = JavaType::try_recursive(&mut it)?;
374 args.push(TypeArg::Extends(Box::new(ty)));
375 }
376 Some('-') => {
377 it.next();
378 let ty = JavaType::try_recursive(&mut it)?;
379 args.push(TypeArg::Super(Box::new(ty)));
380 }
381 Some(_) => {
382 let ty = JavaType::try_recursive(&mut it)?;
383 args.push(TypeArg::Exact(Box::new(ty)));
384 }
385 None => return Err(TypeDescriptorErr::UnexpectedEnd),
386 }
387 }
388 }
389
390 segments.push(ClassSignatureSegment {
391 name: seg_name,
392 args,
393 });
394
395 if it.peek() == Some(&'.') {
396 it.next(); } else {
398 break;
399 }
400 }
401
402 if segments.is_empty() {
403 return Err(TypeDescriptorErr::UnexpectedEnd);
404 }
405
406 let first = segments.remove(0);
407 Ok(ClassSignature {
408 first,
409 suffix: segments,
410 })
411 }
412}
413
414impl ReturnType {
415 pub fn try_recursive<I>(it: &mut Peekable<I>) -> Result<ReturnType, TypeDescriptorErr>
418 where
419 I: Iterator<Item = char>,
420 {
421 let c = it.peek().ok_or(TypeDescriptorErr::UnexpectedEnd)?;
422
423 if *c == 'V' {
424 it.next();
425 return Ok(ReturnType::Void);
426 }
427
428 let java_type = JavaType::try_recursive(it)?;
429 Ok(ReturnType::Type(java_type))
430 }
431}
432
433impl Display for JavaType {
434 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
435 match self {
436 JavaType::Primitive(prim) => {
437 let name = match prim {
438 PrimitiveType::Byte => "byte",
439 PrimitiveType::Char => "char",
440 PrimitiveType::Double => "double",
441 PrimitiveType::Float => "float",
442 PrimitiveType::Int => "int",
443 PrimitiveType::Long => "long",
444 PrimitiveType::Short => "short",
445 PrimitiveType::Boolean => "boolean",
446 };
447 write!(f, "{}", name)
448 }
449
450 JavaType::Instance(name) => write!(f, "{}", name.replace('/', ".")),
451
452 JavaType::GenericInstance(sig) => {
453 write!(f, "{}", sig.first.name.replace('/', "."))?;
455 if !sig.first.args.is_empty() {
456 write!(f, "<")?;
457 for (i, a) in sig.first.args.iter().enumerate() {
458 if i > 0 {
459 write!(f, ", ")?;
460 }
461 write!(f, "{a}")?;
462 }
463 write!(f, ">")?;
464 }
465 for seg in &sig.suffix {
467 write!(f, ".{}", seg.name)?;
468 if !seg.args.is_empty() {
469 write!(f, "<")?;
470 for (i, a) in seg.args.iter().enumerate() {
471 if i > 0 {
472 write!(f, ", ")?;
473 }
474 write!(f, "{a}")?;
475 }
476 write!(f, ">")?;
477 }
478 }
479 Ok(())
480 }
481
482 JavaType::TypeVar(name) => write!(f, "{}", name),
483
484 JavaType::Array(elem) => write!(f, "{}[]", elem),
485 }
486 }
487}
488
489impl Display for ReturnType {
490 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
491 match self {
492 ReturnType::Void => write!(f, "void"),
493 ReturnType::Type(java_type) => write!(f, "{}", java_type),
494 }
495 }
496}
497
498impl fmt::Display for TypeArg {
499 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
500 match self {
501 TypeArg::Any => write!(f, "?"),
502 TypeArg::Extends(t) => write!(f, "? extends {}", t),
503 TypeArg::Super(t) => write!(f, "? super {}", t),
504 TypeArg::Exact(t) => write!(f, "{t}"),
505 }
506 }
507}
508
509#[cfg(test)]
510mod tests {
511 use super::*;
512
513 fn parse_one_descriptor(s: &str) -> Result<ReturnType, TypeDescriptorErr> {
514 let mut it = s.chars().peekable();
515 ReturnType::try_recursive(&mut it)
516 }
517
518 fn parse_one_java(s: &str) -> Result<JavaType, TypeDescriptorErr> {
519 let mut it = s.chars().peekable();
520 JavaType::try_recursive(&mut it)
521 }
522
523 fn parse_one_return(s: &str) -> Result<ReturnType, TypeDescriptorErr> {
524 let mut it = s.chars().peekable();
525 ReturnType::try_recursive(&mut it)
526 }
527
528 fn parse_and_rest(s: &str) -> (Result<ReturnType, TypeDescriptorErr>, String) {
529 let mut it = s.chars().peekable();
530 let res = ReturnType::try_recursive(&mut it);
531 let rest: String = it.collect();
532 (res, rest)
533 }
534
535 #[test]
536 fn primitives_try_from_char() {
537 let cases = vec![
538 ('B', PrimitiveType::Byte),
539 ('C', PrimitiveType::Char),
540 ('D', PrimitiveType::Double),
541 ('F', PrimitiveType::Float),
542 ('I', PrimitiveType::Int),
543 ('J', PrimitiveType::Long),
544 ('S', PrimitiveType::Short),
545 ('Z', PrimitiveType::Boolean),
546 ];
547 for (ch, ty) in cases {
548 assert_eq!(PrimitiveType::try_from(ch), Ok(ty));
549 }
550 assert!(PrimitiveType::try_from('Q').is_err());
552 assert!(PrimitiveType::try_from('V').is_err());
554 }
555
556 #[test]
557 fn parse_void_descriptor() {
558 assert_eq!(parse_one_descriptor("V").unwrap(), ReturnType::Void);
559 }
560
561 #[test]
562 fn parse_void_return() {
563 assert_eq!(parse_one_return("V").unwrap(), ReturnType::Void);
564 }
565
566 #[test]
567 fn parse_void_java_type_should_fail() {
568 assert!(parse_one_java("V").is_err());
569 }
570
571 #[test]
572 fn parse_instance_object() {
573 assert_eq!(
574 parse_one_descriptor("Ljava/lang/String;").unwrap(),
575 ReturnType::Type(JavaType::Instance("java/lang/String".to_string()))
576 );
577 assert_eq!(
578 parse_one_java("Ljava/lang/String;").unwrap(),
579 JavaType::Instance("java/lang/String".to_string())
580 );
581 }
582
583 #[test]
584 fn parse_array_of_primitive() {
585 assert_eq!(
586 parse_one_descriptor("[I").unwrap(),
587 ReturnType::Type(JavaType::Array(Box::new(JavaType::Primitive(
588 PrimitiveType::Int
589 ))))
590 );
591 assert_eq!(
592 parse_one_java("[I").unwrap(),
593 JavaType::Array(Box::new(JavaType::Primitive(PrimitiveType::Int)))
594 );
595 }
596
597 #[test]
598 fn parse_array_of_object() {
599 assert_eq!(
600 parse_one_descriptor("[Ljava/util/List;").unwrap(),
601 ReturnType::Type(JavaType::Array(Box::new(JavaType::Instance(
602 "java/util/List".to_string()
603 ))))
604 );
605 assert_eq!(
606 parse_one_java("[Ljava/util/List;").unwrap(),
607 JavaType::Array(Box::new(JavaType::Instance("java/util/List".to_string())))
608 );
609 }
610
611 #[test]
612 fn as_descriptor_tests() {
613 let jt = JavaType::Primitive(PrimitiveType::Int);
614 assert_eq!(jt.as_descriptor(), "I");
615
616 let jt = JavaType::Instance("java/lang/String".to_string());
617 assert_eq!(jt.as_descriptor(), "Ljava/lang/String;");
618
619 let jt = JavaType::Array(Box::new(JavaType::Primitive(PrimitiveType::Boolean)));
620 assert_eq!(jt.as_descriptor(), "[Z");
621
622 let jt = JavaType::Array(Box::new(JavaType::Instance("java/util/List".to_string())));
623 assert_eq!(jt.as_descriptor(), "[Ljava/util/List;");
624
625 let jt = JavaType::Array(Box::new(JavaType::Array(Box::new(JavaType::Primitive(
626 PrimitiveType::Int,
627 )))));
628 assert_eq!(jt.as_descriptor(), "[[I");
629
630 let jt = JavaType::Array(Box::new(JavaType::Array(Box::new(JavaType::Array(
631 Box::new(JavaType::Primitive(PrimitiveType::Int)),
632 )))));
633 assert_eq!(jt.as_descriptor(), "[[[I");
634 }
635
636 #[test]
637 fn parse_multi_dimensional_array() {
638 assert_eq!(
639 parse_one_descriptor("[[I").unwrap(),
640 ReturnType::Type(JavaType::Array(Box::new(JavaType::Array(Box::new(
641 JavaType::Primitive(PrimitiveType::Int)
642 )))))
643 );
644 assert_eq!(
645 parse_one_java("[[I").unwrap(),
646 JavaType::Array(Box::new(JavaType::Array(Box::new(JavaType::Primitive(
647 PrimitiveType::Int
648 )))))
649 );
650 assert_eq!(
651 parse_one_java("[[[I").unwrap(),
652 JavaType::Array(Box::new(JavaType::Array(Box::new(JavaType::Array(
653 Box::new(JavaType::Primitive(PrimitiveType::Int))
654 )))))
655 );
656 assert_eq!(
657 parse_one_descriptor("[[Ljava/lang/String;").unwrap(),
658 ReturnType::Type(JavaType::Array(Box::new(JavaType::Array(Box::new(
659 JavaType::Instance("java/lang/String".to_string())
660 )))))
661 );
662 assert_eq!(
663 parse_one_java("[[Ljava/lang/String;").unwrap(),
664 JavaType::Array(Box::new(JavaType::Array(Box::new(JavaType::Instance(
665 "java/lang/String".to_string()
666 )))))
667 );
668 }
669
670 #[test]
671 fn error_unexpected_end_after_l() {
672 let err = parse_one_descriptor("Ljava/lang/String").unwrap_err();
674 assert!(matches!(err, TypeDescriptorErr::UnexpectedEnd));
675 }
676
677 #[test]
678 fn error_unexpected_end_after_array_prefix() {
679 let err = parse_one_descriptor("[").unwrap_err();
680 assert!(matches!(err, TypeDescriptorErr::UnexpectedEnd));
681 }
682
683 #[test]
684 fn error_invalid_type_tag() {
685 let err = parse_one_descriptor("Q").unwrap_err();
686 assert!(matches!(err, TypeDescriptorErr::InvalidType('Q')));
687 }
688
689 #[test]
690 fn consumes_exactly_one_type() {
691 let (res, rest) = parse_and_rest("I[Ljava/lang/String;");
692 assert_eq!(
693 res.unwrap(),
694 ReturnType::Type(JavaType::Primitive(PrimitiveType::Int))
695 );
696 assert_eq!(rest, "[Ljava/lang/String;".to_string()); }
698
699 #[test]
702 fn generic_first_segment_only() {
703 let s = "Ljava/util/List<+Ljava/lang/CharSequence;>;";
704 let t = parse_one_java(s).unwrap();
705 assert_eq!(
706 t,
707 JavaType::GenericInstance(ClassSignature {
708 first: ClassSignatureSegment {
709 name: "java/util/List".into(),
710 args: vec![TypeArg::Extends(Box::new(JavaType::Instance(
711 "java/lang/CharSequence".into()
712 )))],
713 },
714 suffix: vec![],
715 })
716 );
717 }
718
719 #[test]
720 fn generic_with_suffix_and_args() {
721 let s = "Ljava/util/Map<Ljava/lang/String;Ljava/lang/Integer;>.Entry<Ljava/lang/String;>;";
722 let t = parse_one_java(s).unwrap();
723 assert_eq!(
724 t,
725 JavaType::GenericInstance(ClassSignature {
726 first: ClassSignatureSegment {
727 name: "java/util/Map".into(),
728 args: vec![
729 TypeArg::Exact(Box::new(JavaType::Instance("java/lang/String".into()))),
730 TypeArg::Exact(Box::new(JavaType::Instance("java/lang/Integer".into()))),
731 ],
732 },
733 suffix: vec![ClassSignatureSegment {
734 name: "Entry".into(),
735 args: vec![TypeArg::Exact(Box::new(JavaType::Instance(
736 "java/lang/String".into()
737 )))],
738 }],
739 })
740 );
741 }
742
743 #[test]
744 fn suffix_without_args() {
745 let s = "Lpkg/Outer.Inner;";
746 let t = parse_one_java(s).unwrap();
747 assert_eq!(
748 t,
749 JavaType::GenericInstance(ClassSignature {
750 first: ClassSignatureSegment {
751 name: "pkg/Outer".into(),
752 args: vec![]
753 },
754 suffix: vec![ClassSignatureSegment {
755 name: "Inner".into(),
756 args: vec![]
757 }],
758 })
759 );
760 }
761
762 #[test]
763 fn error_unexpected_end_in_type_args() {
764 let err = parse_one_java("Ljava/util/List<").unwrap_err();
766 assert!(matches!(err, TypeDescriptorErr::UnexpectedEnd));
767 }
768
769 #[test]
770 fn error_invalid_after_type_args() {
771 let err = parse_one_java("Ljava/util/List<Ljava/lang/String;>X").unwrap_err();
773 assert!(matches!(err, TypeDescriptorErr::InvalidType('X')));
774 }
775}