1 use std::{fmt, str::FromStr};
2
3 use combine::{
4 between, many, many1, parser, satisfy, token, ParseError, Parser, StdParseResult, Stream,
5 };
6
7 use crate::errors::*;
8
9 /// A primitive java type. These are the things that can be represented without
10 /// an object.
11 #[allow(missing_docs)]
12 #[derive(Eq, PartialEq, Debug, Clone, Copy)]
13 pub enum Primitive {
14 Boolean, // Z
15 Byte, // B
16 Char, // C
17 Double, // D
18 Float, // F
19 Int, // I
20 Long, // J
21 Short, // S
22 Void, // V
23 }
24
25 impl fmt::Display for Primitive {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result26 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
27 match *self {
28 Primitive::Boolean => write!(f, "Z"),
29 Primitive::Byte => write!(f, "B"),
30 Primitive::Char => write!(f, "C"),
31 Primitive::Double => write!(f, "D"),
32 Primitive::Float => write!(f, "F"),
33 Primitive::Int => write!(f, "I"),
34 Primitive::Long => write!(f, "J"),
35 Primitive::Short => write!(f, "S"),
36 Primitive::Void => write!(f, "V"),
37 }
38 }
39 }
40
41 /// Enum representing any java type in addition to method signatures.
42 #[allow(missing_docs)]
43 #[derive(Eq, PartialEq, Debug, Clone)]
44 pub enum JavaType {
45 Primitive(Primitive),
46 Object(String),
47 Array(Box<JavaType>),
48 Method(Box<TypeSignature>),
49 }
50
51 impl FromStr for JavaType {
52 type Err = Error;
53
from_str(s: &str) -> std::result::Result<Self, Self::Err>54 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
55 parser(parse_type)
56 .parse(s)
57 .map(|res| res.0)
58 .map_err(|e| Error::ParseFailed(e, s.to_owned()))
59 }
60 }
61
62 impl fmt::Display for JavaType {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result63 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
64 match *self {
65 JavaType::Primitive(ref ty) => ty.fmt(f),
66 JavaType::Object(ref name) => write!(f, "L{name};"),
67 JavaType::Array(ref ty) => write!(f, "[{ty}"),
68 JavaType::Method(ref m) => m.fmt(f),
69 }
70 }
71 }
72
73 /// Enum representing any java type that may be used as a return value
74 ///
75 /// This type intentionally avoids capturing any heap allocated types (to avoid
76 /// allocations while making JNI method calls) and so it doesn't fully qualify
77 /// the object or array types with a String like `JavaType::Object` does.
78 #[allow(missing_docs)]
79 #[derive(Eq, PartialEq, Debug, Clone)]
80 pub enum ReturnType {
81 Primitive(Primitive),
82 Object,
83 Array,
84 }
85
86 impl FromStr for ReturnType {
87 type Err = Error;
88
from_str(s: &str) -> std::result::Result<Self, Self::Err>89 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
90 parser(parse_return)
91 .parse(s)
92 .map(|res| res.0)
93 .map_err(|e| Error::ParseFailed(e, s.to_owned()))
94 }
95 }
96
97 impl fmt::Display for ReturnType {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result98 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
99 match *self {
100 ReturnType::Primitive(ref ty) => ty.fmt(f),
101 ReturnType::Object => write!(f, "L;"),
102 ReturnType::Array => write!(f, "["),
103 }
104 }
105 }
106
107 /// A method type signature. This is the structure representation of something
108 /// like `(Ljava/lang/String;)Z`. Used by the `call_(object|static)_method`
109 /// functions on jnienv to ensure safety.
110 #[allow(missing_docs)]
111 #[derive(Eq, PartialEq, Debug, Clone)]
112 pub struct TypeSignature {
113 pub args: Vec<JavaType>,
114 pub ret: ReturnType,
115 }
116
117 impl TypeSignature {
118 /// Parse a signature string into a TypeSignature enum.
119 // Clippy suggests implementing `FromStr` or renaming it which is not possible in our case.
120 #[allow(clippy::should_implement_trait)]
from_str<S: AsRef<str>>(s: S) -> Result<TypeSignature>121 pub fn from_str<S: AsRef<str>>(s: S) -> Result<TypeSignature> {
122 Ok(match parser(parse_sig).parse(s.as_ref()).map(|res| res.0) {
123 Ok(JavaType::Method(sig)) => *sig,
124 Err(e) => return Err(Error::ParseFailed(e, s.as_ref().to_owned())),
125 _ => unreachable!(),
126 })
127 }
128 }
129
130 impl fmt::Display for TypeSignature {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result131 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
132 write!(f, "(")?;
133 for a in &self.args {
134 write!(f, "{a}")?;
135 }
136 write!(f, ")")?;
137 write!(f, "{}", self.ret)?;
138 Ok(())
139 }
140 }
141
parse_primitive<S: Stream<Token = char>>(input: &mut S) -> StdParseResult<Primitive, S> where S::Error: ParseError<char, S::Range, S::Position>,142 fn parse_primitive<S: Stream<Token = char>>(input: &mut S) -> StdParseResult<Primitive, S>
143 where
144 S::Error: ParseError<char, S::Range, S::Position>,
145 {
146 let boolean = token('Z').map(|_| Primitive::Boolean);
147 let byte = token('B').map(|_| Primitive::Byte);
148 let char_type = token('C').map(|_| Primitive::Char);
149 let double = token('D').map(|_| Primitive::Double);
150 let float = token('F').map(|_| Primitive::Float);
151 let int = token('I').map(|_| Primitive::Int);
152 let long = token('J').map(|_| Primitive::Long);
153 let short = token('S').map(|_| Primitive::Short);
154 let void = token('V').map(|_| Primitive::Void);
155
156 (boolean
157 .or(byte)
158 .or(char_type)
159 .or(double)
160 .or(float)
161 .or(int)
162 .or(long)
163 .or(short)
164 .or(void))
165 .parse_stream(input)
166 .into()
167 }
168
parse_array<S: Stream<Token = char>>(input: &mut S) -> StdParseResult<JavaType, S> where S::Error: ParseError<char, S::Range, S::Position>,169 fn parse_array<S: Stream<Token = char>>(input: &mut S) -> StdParseResult<JavaType, S>
170 where
171 S::Error: ParseError<char, S::Range, S::Position>,
172 {
173 let marker = token('[');
174 (marker, parser(parse_type))
175 .map(|(_, ty)| JavaType::Array(Box::new(ty)))
176 .parse_stream(input)
177 .into()
178 }
179
parse_object<S: Stream<Token = char>>(input: &mut S) -> StdParseResult<JavaType, S> where S::Error: ParseError<char, S::Range, S::Position>,180 fn parse_object<S: Stream<Token = char>>(input: &mut S) -> StdParseResult<JavaType, S>
181 where
182 S::Error: ParseError<char, S::Range, S::Position>,
183 {
184 let marker = token('L');
185 let end = token(';');
186 let obj = between(marker, end, many1(satisfy(|c| c != ';')));
187
188 obj.map(JavaType::Object).parse_stream(input).into()
189 }
190
parse_type<S: Stream<Token = char>>(input: &mut S) -> StdParseResult<JavaType, S> where S::Error: ParseError<char, S::Range, S::Position>,191 fn parse_type<S: Stream<Token = char>>(input: &mut S) -> StdParseResult<JavaType, S>
192 where
193 S::Error: ParseError<char, S::Range, S::Position>,
194 {
195 parser(parse_primitive)
196 .map(JavaType::Primitive)
197 .or(parser(parse_array))
198 .or(parser(parse_object))
199 .or(parser(parse_sig))
200 .parse_stream(input)
201 .into()
202 }
203
parse_return<S: Stream<Token = char>>(input: &mut S) -> StdParseResult<ReturnType, S> where S::Error: ParseError<char, S::Range, S::Position>,204 fn parse_return<S: Stream<Token = char>>(input: &mut S) -> StdParseResult<ReturnType, S>
205 where
206 S::Error: ParseError<char, S::Range, S::Position>,
207 {
208 parser(parse_primitive)
209 .map(ReturnType::Primitive)
210 .or(parser(parse_array).map(|_| ReturnType::Array))
211 .or(parser(parse_object).map(|_| ReturnType::Object))
212 .parse_stream(input)
213 .into()
214 }
215
parse_args<S: Stream<Token = char>>(input: &mut S) -> StdParseResult<Vec<JavaType>, S> where S::Error: ParseError<char, S::Range, S::Position>,216 fn parse_args<S: Stream<Token = char>>(input: &mut S) -> StdParseResult<Vec<JavaType>, S>
217 where
218 S::Error: ParseError<char, S::Range, S::Position>,
219 {
220 between(token('('), token(')'), many(parser(parse_type)))
221 .parse_stream(input)
222 .into()
223 }
224
parse_sig<S: Stream<Token = char>>(input: &mut S) -> StdParseResult<JavaType, S> where S::Error: ParseError<char, S::Range, S::Position>,225 fn parse_sig<S: Stream<Token = char>>(input: &mut S) -> StdParseResult<JavaType, S>
226 where
227 S::Error: ParseError<char, S::Range, S::Position>,
228 {
229 (parser(parse_args), parser(parse_return))
230 .map(|(a, r)| TypeSignature { args: a, ret: r })
231 .map(|sig| JavaType::Method(Box::new(sig)))
232 .parse_stream(input)
233 .into()
234 }
235
236 #[cfg(test)]
237 mod test {
238 use super::*;
239
240 #[test]
test_parser()241 fn test_parser() {
242 let inputs = [
243 "(Ljava/lang/String;I)V",
244 "[Lherp;",
245 // fails because the return type does not contain the class name: "(IBVZ)L;"
246 // "(IBVZ)Ljava/lang/String;",
247 ];
248
249 for each in inputs.iter() {
250 let res = JavaType::from_str(each).unwrap();
251 println!("{res:#?}");
252 let s = format!("{res}");
253 assert_eq!(s, *each);
254 let res2 = JavaType::from_str(each).unwrap();
255 println!("{res2:#?}");
256 assert_eq!(res2, res);
257 }
258 }
259
260 #[test]
test_parser_invalid_signature()261 fn test_parser_invalid_signature() {
262 let signature = "()Ljava/lang/List"; // no semicolon
263 let res = JavaType::from_str(signature);
264
265 match res {
266 Ok(any) => {
267 panic!("Unexpected result: {}", any);
268 }
269 Err(err) => {
270 assert!(err.to_string().contains("input: ()Ljava/lang/List"));
271 }
272 }
273 }
274 }
275