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