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 /// A method type signature. This is the structure representation of something
74 /// like `(Ljava/lang/String;)Z`. Used by the `call_(object|static)_method`
75 /// functions on jnienv to ensure safety.
76 #[allow(missing_docs)]
77 #[derive(Eq, PartialEq, Debug, Clone)]
78 pub struct TypeSignature {
79     pub args: Vec<JavaType>,
80     pub ret: JavaType,
81 }
82 
83 impl TypeSignature {
84     /// Parse a signature string into a TypeSignature enum.
85     // Clippy suggests implementing `FromStr` or renaming it which is not possible in our case.
86     #[allow(clippy::should_implement_trait)]
from_str<S: AsRef<str>>(s: S) -> Result<TypeSignature>87     pub fn from_str<S: AsRef<str>>(s: S) -> Result<TypeSignature> {
88         Ok(match parser(parse_sig).parse(s.as_ref()).map(|res| res.0) {
89             Ok(JavaType::Method(sig)) => *sig,
90             Err(e) => return Err(Error::ParseFailed(e, s.as_ref().to_owned())),
91             _ => unreachable!(),
92         })
93     }
94 }
95 
96 impl fmt::Display for TypeSignature {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result97     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
98         write!(f, "(")?;
99         for a in &self.args {
100             write!(f, "{}", a)?;
101         }
102         write!(f, ")")?;
103         write!(f, "{}", self.ret)?;
104         Ok(())
105     }
106 }
107 
parse_primitive<S: Stream<Token = char>>(input: &mut S) -> StdParseResult<JavaType, S> where S::Error: ParseError<char, S::Range, S::Position>,108 fn parse_primitive<S: Stream<Token = char>>(input: &mut S) -> StdParseResult<JavaType, S>
109 where
110     S::Error: ParseError<char, S::Range, S::Position>,
111 {
112     let boolean = token('Z').map(|_| Primitive::Boolean);
113     let byte = token('B').map(|_| Primitive::Byte);
114     let char_type = token('C').map(|_| Primitive::Char);
115     let double = token('D').map(|_| Primitive::Double);
116     let float = token('F').map(|_| Primitive::Float);
117     let int = token('I').map(|_| Primitive::Int);
118     let long = token('J').map(|_| Primitive::Long);
119     let short = token('S').map(|_| Primitive::Short);
120     let void = token('V').map(|_| Primitive::Void);
121 
122     (boolean
123         .or(byte)
124         .or(char_type)
125         .or(double)
126         .or(float)
127         .or(int)
128         .or(long)
129         .or(short)
130         .or(void))
131     .map(JavaType::Primitive)
132     .parse_stream(input)
133     .into()
134 }
135 
parse_array<S: Stream<Token = char>>(input: &mut S) -> StdParseResult<JavaType, S> where S::Error: ParseError<char, S::Range, S::Position>,136 fn parse_array<S: Stream<Token = char>>(input: &mut S) -> StdParseResult<JavaType, S>
137 where
138     S::Error: ParseError<char, S::Range, S::Position>,
139 {
140     let marker = token('[');
141     (marker, parser(parse_type))
142         .map(|(_, ty)| JavaType::Array(Box::new(ty)))
143         .parse_stream(input)
144         .into()
145 }
146 
parse_object<S: Stream<Token = char>>(input: &mut S) -> StdParseResult<JavaType, S> where S::Error: ParseError<char, S::Range, S::Position>,147 fn parse_object<S: Stream<Token = char>>(input: &mut S) -> StdParseResult<JavaType, S>
148 where
149     S::Error: ParseError<char, S::Range, S::Position>,
150 {
151     let marker = token('L');
152     let end = token(';');
153     let obj = between(marker, end, many1(satisfy(|c| c != ';')));
154 
155     obj.map(JavaType::Object).parse_stream(input).into()
156 }
157 
parse_type<S: Stream<Token = char>>(input: &mut S) -> StdParseResult<JavaType, S> where S::Error: ParseError<char, S::Range, S::Position>,158 fn parse_type<S: Stream<Token = char>>(input: &mut S) -> StdParseResult<JavaType, S>
159 where
160     S::Error: ParseError<char, S::Range, S::Position>,
161 {
162     parser(parse_primitive)
163         .or(parser(parse_array))
164         .or(parser(parse_object))
165         .or(parser(parse_sig))
166         .parse_stream(input)
167         .into()
168 }
169 
parse_args<S: Stream<Token = char>>(input: &mut S) -> StdParseResult<Vec<JavaType>, S> where S::Error: ParseError<char, S::Range, S::Position>,170 fn parse_args<S: Stream<Token = char>>(input: &mut S) -> StdParseResult<Vec<JavaType>, S>
171 where
172     S::Error: ParseError<char, S::Range, S::Position>,
173 {
174     between(token('('), token(')'), many(parser(parse_type)))
175         .parse_stream(input)
176         .into()
177 }
178 
parse_sig<S: Stream<Token = char>>(input: &mut S) -> StdParseResult<JavaType, S> where S::Error: ParseError<char, S::Range, S::Position>,179 fn parse_sig<S: Stream<Token = char>>(input: &mut S) -> StdParseResult<JavaType, S>
180 where
181     S::Error: ParseError<char, S::Range, S::Position>,
182 {
183     (parser(parse_args), parser(parse_type))
184         .map(|(a, r)| TypeSignature { args: a, ret: r })
185         .map(|sig| JavaType::Method(Box::new(sig)))
186         .parse_stream(input)
187         .into()
188 }
189 
190 #[cfg(test)]
191 mod test {
192     use super::*;
193 
194     #[test]
test_parser()195     fn test_parser() {
196         let inputs = [
197             "(Ljava/lang/String;I)V",
198             "[Lherp;",
199             "(IBVZ)Ljava/lang/String;",
200         ];
201 
202         for each in inputs.iter() {
203             let res = JavaType::from_str(*each).unwrap();
204             println!("{:#?}", res);
205             let s = format!("{}", res);
206             assert_eq!(s, *each);
207             let res2 = JavaType::from_str(*each).unwrap();
208             println!("{:#?}", res2);
209             assert_eq!(res2, res);
210         }
211     }
212 
213     #[test]
test_parser_invalid_signature()214     fn test_parser_invalid_signature() {
215         let signature = "()Ljava/lang/List"; // no semicolon
216         let res = JavaType::from_str(signature);
217 
218         match res {
219             Ok(any) => {
220                 panic!("Unexpected result: {}", any);
221             }
222             Err(err) => {
223                 assert!(err.to_string().contains("input: ()Ljava/lang/List"));
224             }
225         }
226     }
227 }
228