Skip to main content

lvm_common/
descriptor.rs

1use crate::error::MethodDescriptorErr;
2use crate::jtype::{JavaType, ReturnType};
3
4/// Field descriptor - represents a field type (cannot be void)
5/// https://docs.oracle.com/javase/specs/jvms/se25/html/jvms-4.html#jvms-4.3.2
6pub type FieldDescriptor = JavaType;
7
8/// Method descriptor - represents a method signature
9/// https://docs.oracle.com/javase/specs/jvms/se25/html/jvms-4.html#jvms-4.3.3
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct MethodDescriptor {
12    pub params: Vec<JavaType>,
13    pub ret: ReturnType,
14}
15
16impl MethodDescriptor {
17    pub fn to_java_signature(&self, class_name: &str, method_name: &str) -> String {
18        let mut buffer = String::with_capacity(
19            class_name.len() + method_name.len() + self.params.len() * 10 + 10,
20        );
21
22        use std::fmt::Write;
23        write!(buffer, "{} ", self.ret).unwrap();
24
25        for c in class_name.chars() {
26            buffer.push(if c == '/' { '.' } else { c });
27        }
28
29        write!(buffer, ".{}(", method_name).unwrap();
30
31        for (i, p) in self.params.iter().enumerate() {
32            if i > 0 {
33                buffer.push_str(", ");
34            }
35            write!(buffer, "{}", p).unwrap();
36        }
37
38        buffer.push(')');
39        buffer
40    }
41}
42
43impl TryFrom<&str> for MethodDescriptor {
44    type Error = MethodDescriptorErr;
45
46    fn try_from(desc: &str) -> Result<Self, Self::Error> {
47        let mut chars = desc.chars().peekable();
48
49        if chars.next() != Some('(') {
50            return Err(MethodDescriptorErr::ShouldStartWithParentheses(
51                desc.to_string(),
52            ));
53        }
54
55        let mut params = Vec::new();
56        loop {
57            match chars.peek() {
58                Some(')') => {
59                    chars.next();
60                    break;
61                }
62                Some(_) => params.push(
63                    JavaType::try_recursive(&mut chars)
64                        .map_err(|e| MethodDescriptorErr::Type(desc.to_string(), e))?,
65                ),
66                None => {
67                    return Err(MethodDescriptorErr::MissingClosingParenthesis(
68                        desc.to_string(),
69                    ));
70                }
71            }
72        }
73
74        let ret = ReturnType::try_recursive(&mut chars)
75            .map_err(|e| MethodDescriptorErr::Type(desc.to_string(), e))?;
76
77        if chars.next().is_some() {
78            return Err(MethodDescriptorErr::TrailingCharacters);
79        }
80
81        Ok(MethodDescriptor { params, ret })
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88    use crate::jtype::PrimitiveType;
89
90    // Java: void add(int, int)
91    #[test]
92    fn parse_two_ints_void() {
93        // given
94        let signature = "(II)V";
95        let expected_param = vec![
96            JavaType::Primitive(PrimitiveType::Int),
97            JavaType::Primitive(PrimitiveType::Int),
98        ];
99        let expected_ret = ReturnType::Void;
100
101        // when
102        let md = MethodDescriptor::try_from(signature).unwrap();
103
104        // then
105        assert_eq!(md.params, expected_param);
106        assert_eq!(md.ret, expected_ret);
107    }
108
109    // Java: int getCount()
110    #[test]
111    fn parse_no_params_int_return() {
112        // given
113        let signature = "()I";
114        let expected_param: Vec<JavaType> = Vec::new();
115        let expected_ret = ReturnType::Type(JavaType::Primitive(PrimitiveType::Int));
116
117        // when
118        let md = MethodDescriptor::try_from(signature).unwrap();
119
120        // then
121        assert_eq!(md.params, expected_param);
122        assert_eq!(md.ret, expected_ret);
123    }
124
125    // Java: String echo(String)
126    #[test]
127    fn parse_string_param_string_return() {
128        // given
129        let signature = "(Ljava/lang/String;)Ljava/lang/String;";
130        let expected_param = vec![JavaType::Instance("java/lang/String".into())];
131        let expected_ret = ReturnType::Type(JavaType::Instance("java/lang/String".into()));
132
133        // when
134        let md = MethodDescriptor::try_from(signature).unwrap();
135
136        // then
137        assert_eq!(md.params, expected_param);
138        assert_eq!(md.ret, expected_ret);
139    }
140
141    // Java: int[] process(int, String[])
142    #[test]
143    fn parse_array_param_and_return() {
144        // given
145        let signature = "(I[Ljava/lang/String;)[I";
146        let expected_param = vec![
147            JavaType::Primitive(PrimitiveType::Int),
148            JavaType::Array(Box::new(JavaType::Instance("java/lang/String".into()))),
149        ];
150        let expected_ret = ReturnType::Type(JavaType::Array(Box::new(JavaType::Primitive(
151            PrimitiveType::Int,
152        ))));
153
154        // when
155        let md = MethodDescriptor::try_from(signature).unwrap();
156
157        // then
158        assert_eq!(md.params, expected_param);
159        assert_eq!(md.ret, expected_ret);
160    }
161
162    // Java: Object[][] flatten(Object[][])
163    #[test]
164    fn parse_multi_dimensional_arrays() {
165        // given
166        let signature = "([[Ljava/lang/Object;)[[Ljava/lang/Object;";
167        let obj = JavaType::Instance("java/lang/Object".into());
168        let two_d_obj = JavaType::Array(Box::new(JavaType::Array(Box::new(obj.clone()))));
169        let expected_param = vec![two_d_obj.clone()];
170        let expected_ret = ReturnType::Type(two_d_obj);
171
172        // when
173        let md = MethodDescriptor::try_from(signature).unwrap();
174
175        // then
176        assert_eq!(md.params, expected_param);
177        assert_eq!(md.ret, expected_ret);
178    }
179
180    // Test that void parameters are rejected
181    #[test]
182    fn parse_void_param_should_fail() {
183        let signature = "(V)I";
184        let result = MethodDescriptor::try_from(signature);
185        assert!(result.is_err());
186    }
187
188    // Test field descriptor parsing
189    #[test]
190    fn parse_field_descriptor_int() {
191        let mut chars = "I".chars().peekable();
192        let field = JavaType::try_recursive(&mut chars).unwrap();
193        assert_eq!(field, JavaType::Primitive(PrimitiveType::Int));
194    }
195
196    #[test]
197    fn parse_field_descriptor_string() {
198        let mut chars = "Ljava/lang/String;".chars().peekable();
199        let field = JavaType::try_recursive(&mut chars).unwrap();
200        assert_eq!(field, JavaType::Instance("java/lang/String".into()));
201    }
202
203    #[test]
204    fn parse_field_descriptor_array() {
205        let mut chars = "[I".chars().peekable();
206        let field = JavaType::try_recursive(&mut chars).unwrap();
207        assert_eq!(
208            field,
209            JavaType::Array(Box::new(JavaType::Primitive(PrimitiveType::Int)))
210        );
211    }
212
213    // Test that void field descriptor is rejected
214    #[test]
215    fn parse_field_descriptor_void_should_fail() {
216        let mut chars = "V".chars().peekable();
217        let result = JavaType::try_recursive(&mut chars);
218        assert!(result.is_err());
219    }
220}