java_bin_name/
ty.rs

1use std::fmt::{Debug, Display};
2
3use crate::{Cursor, Parse, class::ClassName};
4
5/// Error type marking the leading character of a field descriptor is invalid.
6#[derive(Debug, Clone)]
7pub struct UnknownFieldType(char);
8
9impl std::error::Error for UnknownFieldType {}
10
11impl Display for UnknownFieldType {
12    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
13        write!(f, "unknown field type '{}'", self.0)
14    }
15}
16
17/// The type of a field, parameter, local variable, or value.
18///
19/// See [JVMS 4.3.2](https://docs.oracle.com/javase/specs/jvms/se25/html/jvms-4.html#jvms-4.3.2).
20#[derive(Clone, PartialEq, Eq, Hash)]
21#[allow(missing_docs)] // pointless
22pub enum FieldType<'a> {
23    Byte,
24    Char,
25    Double,
26    Float,
27    Int,
28    Long,
29    Short,
30    Boolean,
31    Class(Box<ClassName<'a>>),
32    Array(Box<Self>),
33}
34
35impl Display for FieldType<'_> {
36    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37        match self {
38            Self::Byte => write!(f, "B"),
39            Self::Char => write!(f, "C"),
40            Self::Double => write!(f, "D"),
41            Self::Float => write!(f, "F"),
42            Self::Int => write!(f, "I"),
43            Self::Long => write!(f, "J"),
44            Self::Short => write!(f, "S"),
45            Self::Boolean => write!(f, "Z"),
46            Self::Class(class_name) => write!(f, "L{class_name};"),
47            Self::Array(field_ty) => write!(f, "[{field_ty}"),
48        }
49    }
50}
51
52impl<'a> Parse<'a> for FieldType<'a> {
53    type Error = UnknownFieldType;
54
55    fn parse_from(cursor: &mut Cursor<'a>) -> Result<Self, Self::Error> {
56        let leading = cursor.get_char();
57        match leading {
58            'B' => Ok(Self::Byte),
59            'C' => Ok(Self::Char),
60            'D' => Ok(Self::Double),
61            'F' => Ok(Self::Float),
62            'I' => Ok(Self::Int),
63            'J' => Ok(Self::Long),
64            'S' => Ok(Self::Short),
65            'Z' => Ok(Self::Boolean),
66            'L' => cursor
67                .try_advance(|s| s.split_once(';').ok_or(UnknownFieldType('L')))
68                .map(|s| {
69                    Self::Class(Box::new(
70                        ClassName::parse_from(&mut Cursor::new(s)).unwrap(),
71                    ))
72                }),
73            '[' => Self::parse_from(cursor).map(|t| Self::Array(Box::new(t))),
74            _ => Err(UnknownFieldType(leading)),
75        }
76    }
77}
78
79impl Debug for FieldType<'_> {
80    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81        match self {
82            Self::Byte => write!(f, "byte"),
83            Self::Char => write!(f, "char"),
84            Self::Double => write!(f, "double"),
85            Self::Float => write!(f, "float"),
86            Self::Int => write!(f, "int"),
87            Self::Long => write!(f, "long"),
88            Self::Short => write!(f, "short"),
89            Self::Boolean => write!(f, "boolean"),
90            Self::Class(arg0) => Debug::fmt(arg0, f),
91            Self::Array(arg0) => {
92                Debug::fmt(arg0, f)?;
93                write!(f, "[]")
94            }
95        }
96    }
97}
98
99#[cfg(test)]
100mod tests {
101    use crate::{CanonicalClassName, ClassName, FieldType, ReprForm, parse, validate_rw};
102
103    #[test]
104    fn primitives() {
105        macro_rules! primitives {
106            ($($v:ident,$l:expr),*$(,)?) => {
107                $(
108                assert_eq!(parse::<'_, FieldType<'_>>($l).unwrap(), FieldType::$v);
109                validate_rw::<'_, FieldType<'_>>($l);
110                )*
111            };
112        }
113
114        primitives! {
115            Byte, "B",
116            Char, "C",
117            Double, "D",
118            Float, "F",
119            Int, "I",
120            Long, "J",
121            Short, "S",
122            Boolean, "Z",
123        }
124    }
125
126    #[test]
127    fn class() {
128        assert_eq!(
129            parse::<'_, FieldType<'_>>("Ljava/lang/Object;").unwrap(),
130            FieldType::Class(Box::new(ClassName::TopLevel(CanonicalClassName {
131                package: Some("java/lang"),
132                simple: "Object",
133                form: ReprForm::Internal,
134            })))
135        );
136        validate_rw::<'_, FieldType<'_>>("Ljava/lang/Object;");
137    }
138
139    #[test]
140    fn array_primitive() {
141        assert_eq!(
142            parse::<'_, FieldType<'_>>("[I").unwrap(),
143            FieldType::Array(Box::new(FieldType::Int))
144        );
145        validate_rw::<'_, FieldType<'_>>("[I");
146    }
147
148    #[test]
149    fn array_primitive_2d() {
150        assert_eq!(
151            parse::<'_, FieldType<'_>>("[[I").unwrap(),
152            FieldType::Array(Box::new(FieldType::Array(Box::new(FieldType::Int))))
153        );
154        validate_rw::<'_, FieldType<'_>>("[[I");
155    }
156
157    #[test]
158    fn array_class() {
159        assert_eq!(
160            parse::<'_, FieldType<'_>>("[Ljava/lang/Object;").unwrap(),
161            FieldType::Array(Box::new(FieldType::Class(Box::new(ClassName::TopLevel(
162                CanonicalClassName {
163                    package: Some("java/lang"),
164                    simple: "Object",
165                    form: ReprForm::Internal,
166                }
167            )))))
168        );
169        validate_rw::<'_, FieldType<'_>>("[Ljava/lang/Object;");
170    }
171
172    #[test]
173    fn array_class_2d() {
174        assert_eq!(
175            parse::<'_, FieldType<'_>>("[[Ljava/lang/Object;").unwrap(),
176            FieldType::Array(Box::new(FieldType::Array(Box::new(FieldType::Class(
177                Box::new(ClassName::TopLevel(CanonicalClassName {
178                    package: Some("java/lang"),
179                    simple: "Object",
180                    form: ReprForm::Internal,
181                }))
182            )))))
183        );
184        validate_rw::<'_, FieldType<'_>>("[[Ljava/lang/Object;");
185    }
186}