Skip to main content

java_bin_name/
method.rs

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