java_bin_name/
method.rs

1use std::fmt::{Debug, Display};
2
3use crate::{Cursor, Parse, UnknownFieldType, ty::FieldType};
4
5/// Error thrown when parsing a method descriptor.
6#[derive(Debug, Clone)]
7pub enum InvalidMethodDescriptor {
8    /// Brackets not exist or not enclosed.
9    BrokenBrackets,
10    /// Error when parsing field type.
11    UnknownFieldTy(UnknownFieldType),
12}
13
14impl std::error::Error for InvalidMethodDescriptor {}
15
16impl From<UnknownFieldType> for InvalidMethodDescriptor {
17    fn from(value: UnknownFieldType) -> Self {
18        Self::UnknownFieldTy(value)
19    }
20}
21
22impl Display for InvalidMethodDescriptor {
23    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24        match self {
25            Self::BrokenBrackets => write!(f, "broken brackets"),
26            Self::UnknownFieldTy(unknown_field_ty) => {
27                write!(f, "{unknown_field_ty}")
28            }
29        }
30    }
31}
32
33/// Descriptor of a method despite of its signature.
34///
35/// See [JVMS 4.3.3](https://docs.oracle.com/javase/specs/jvms/se25/html/jvms-4.html#jvms-4.3.3).
36#[derive(Clone, PartialEq, Eq, Hash)]
37pub struct MethodDescriptor<'a> {
38    /// Zero or more parameter descriptors, representing the types of parameters that the method takes.
39    pub params: Box<[FieldType<'a>]>,
40    /// The return descriptor.
41    pub ret: MethodReturnDescriptor<'a>,
42}
43
44impl Display for MethodDescriptor<'_> {
45    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46        write!(f, "(")?;
47        for param in &self.params {
48            write!(f, "{param}")?;
49        }
50        write!(f, "){}", self.ret)
51    }
52}
53
54impl<'a> Parse<'a> for MethodDescriptor<'a> {
55    type Error = InvalidMethodDescriptor;
56
57    fn parse_from(cursor: &mut Cursor<'a>) -> Result<Self, Self::Error> {
58        if cursor.get_char() != '(' {
59            return Err(InvalidMethodDescriptor::BrokenBrackets);
60        }
61        let mut params_raw = Cursor::new(cursor.try_advance(|s| {
62            s.split_once(')')
63                .ok_or(InvalidMethodDescriptor::BrokenBrackets)
64        })?);
65        let mut params = vec![];
66        while !params_raw.get().is_empty() {
67            params.push(FieldType::parse_from(&mut params_raw)?);
68        }
69        Ok(Self {
70            params: params.into_boxed_slice(),
71            ret: MethodReturnDescriptor::parse_from(cursor)?,
72        })
73    }
74}
75
76/// Method return type.
77#[derive(Clone, PartialEq, Eq, Hash)]
78pub enum MethodReturnDescriptor<'a> {
79    /// Represents to `VoidDescriptor` in JVMS.
80    Void,
81    /// Valid return type.
82    Type(FieldType<'a>),
83}
84
85impl Display for MethodReturnDescriptor<'_> {
86    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87        match self {
88            MethodReturnDescriptor::Void => write!(f, "V"),
89            MethodReturnDescriptor::Type(field_ty) => write!(f, "{field_ty}"),
90        }
91    }
92}
93
94impl<'a> Parse<'a> for MethodReturnDescriptor<'a> {
95    type Error = UnknownFieldType;
96
97    fn parse_from(cursor: &mut Cursor<'a>) -> Result<Self, Self::Error> {
98        if cursor.get().chars().next().is_some_and(|c| c == 'V') {
99            cursor.advance_by('V'.len_utf8());
100            Ok(Self::Void)
101        } else {
102            FieldType::parse_from(cursor).map(Self::Type)
103        }
104    }
105}
106
107impl Debug for MethodReturnDescriptor<'_> {
108    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109        match self {
110            Self::Void => write!(f, "void"),
111            Self::Type(ty) => Debug::fmt(ty, f),
112        }
113    }
114}
115
116impl Debug for MethodDescriptor<'_> {
117    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118        write!(f, "method(")?;
119        let mut it = self.params.iter();
120        if let Some(first) = it.next() {
121            Debug::fmt(first, f)?;
122        }
123        for param in it {
124            write!(f, ", ")?;
125            Debug::fmt(param, f)?;
126        }
127        write!(f, ") -> ")?;
128        Debug::fmt(&self.ret, f)
129    }
130}
131
132#[cfg(test)]
133mod tests {
134    use crate::{
135        CanonicalClassName, ClassName, FieldType, MethodDescriptor, MethodReturnDescriptor,
136        ReprForm, parse, validate_rw,
137    };
138
139    #[test]
140    fn return_desc_void() {
141        assert_eq!(
142            parse::<'_, MethodReturnDescriptor<'_>>("V").unwrap(),
143            MethodReturnDescriptor::Void
144        );
145        validate_rw::<'_, MethodReturnDescriptor<'_>>("V");
146    }
147
148    #[test]
149    fn return_desc_primitive() {
150        assert_eq!(
151            parse::<'_, MethodReturnDescriptor<'_>>("I").unwrap(),
152            MethodReturnDescriptor::Type(FieldType::Int)
153        );
154        validate_rw::<'_, MethodReturnDescriptor<'_>>("I");
155    }
156
157    #[test]
158    fn empty_to_void() {
159        assert_eq!(
160            parse::<'_, MethodDescriptor<'_>>("()V").unwrap(),
161            MethodDescriptor {
162                params: Box::new([]),
163                ret: MethodReturnDescriptor::Void
164            }
165        );
166        validate_rw::<'_, MethodDescriptor<'_>>("()V");
167    }
168
169    #[test]
170    fn mixed() {
171        assert_eq!(
172            parse::<'_, MethodDescriptor<'_>>(
173                "(I[BLjava/lang/String;Ljava/lang/Object;Z)[Ljava/lang/String;"
174            )
175            .unwrap(),
176            MethodDescriptor {
177                params: Box::new([
178                    FieldType::Int,
179                    FieldType::Array(Box::new(FieldType::Byte)),
180                    FieldType::Class(Box::new(ClassName::TopLevel(CanonicalClassName {
181                        package: Some("java/lang"),
182                        simple: "String",
183                        form: ReprForm::Internal
184                    }))),
185                    FieldType::Class(Box::new(ClassName::TopLevel(CanonicalClassName {
186                        package: Some("java/lang"),
187                        simple: "Object",
188                        form: ReprForm::Internal
189                    }))),
190                    FieldType::Boolean,
191                ]),
192                ret: MethodReturnDescriptor::Type(FieldType::Array(Box::new(FieldType::Class(
193                    Box::new(ClassName::TopLevel(CanonicalClassName {
194                        package: Some("java/lang"),
195                        simple: "String",
196                        form: ReprForm::Internal
197                    }))
198                ))))
199            }
200        );
201        validate_rw::<'_, MethodDescriptor<'_>>(
202            "(I[BLjava/lang/String;Ljava/lang/Object;Z)[Ljava/lang/String;",
203        );
204    }
205}