mirror of
https://github.com/Noratrieb/coldsquare.git
synced 2026-01-14 16:35:10 +01:00
Field and Method descriptor parsing
with tests
This commit is contained in:
parent
a1620476d8
commit
54ca19a45a
2 changed files with 263 additions and 16 deletions
|
|
@ -1,23 +1,144 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
struct MethodSignature {
|
||||
args: Vec<Type>,
|
||||
return_t: Type,
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ParseErr(pub Cow<'static, str>);
|
||||
|
||||
impl ParseErr {
|
||||
pub fn str(str: &'static str) -> Self {
|
||||
Self(Cow::Borrowed(str))
|
||||
}
|
||||
pub fn string(str: String) -> Self {
|
||||
Self(Cow::Owned(str))
|
||||
}
|
||||
}
|
||||
|
||||
/// A Java type, found in signatures
|
||||
enum Type {
|
||||
/// A field descriptor for the type of a field in a class
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub struct FieldDescriptor(pub FieldType);
|
||||
|
||||
/// The type of a field or method parameter
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub enum FieldType {
|
||||
/// B
|
||||
Byte,
|
||||
/// C
|
||||
Char,
|
||||
/// D
|
||||
Double,
|
||||
/// F
|
||||
Float,
|
||||
/// I
|
||||
Int,
|
||||
/// J
|
||||
Long,
|
||||
/// L `ClassName` ;
|
||||
Object(String),
|
||||
/// S
|
||||
Short,
|
||||
/// Z
|
||||
Boolean,
|
||||
/// [
|
||||
Array(Box<Self>),
|
||||
}
|
||||
|
||||
/// A method descriptor for the type of a method in a class
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub struct MethodDescriptor {
|
||||
parameters: Vec<FieldType>,
|
||||
return_: MethodType,
|
||||
}
|
||||
|
||||
/// The type of a method
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub enum MethodType {
|
||||
Some(FieldType),
|
||||
/// V
|
||||
Void,
|
||||
/// B
|
||||
Boolean,
|
||||
Byte,
|
||||
Short,
|
||||
Int,
|
||||
Long,
|
||||
Float,
|
||||
Double,
|
||||
Object,
|
||||
/// [
|
||||
Array(Box<Type>),
|
||||
}
|
||||
|
||||
impl FromStr for FieldDescriptor {
|
||||
type Err = ParseErr;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Self(FieldType::from_char_iter(&mut s.chars())?))
|
||||
}
|
||||
}
|
||||
|
||||
impl FieldType {
|
||||
/// Consumes as much chars as needed from the char iterator and tries to parse itself
|
||||
pub fn from_char_iter<I>(chars: &mut I) -> Result<Self, ParseErr>
|
||||
where
|
||||
I: Iterator<Item = char>,
|
||||
{
|
||||
let first = chars.next().ok_or_else(|| ParseErr::str("Empty string"))?;
|
||||
Ok(match first {
|
||||
'B' => Self::Byte,
|
||||
'C' => Self::Char,
|
||||
'D' => Self::Double,
|
||||
'F' => Self::Float,
|
||||
'I' => Self::Int,
|
||||
'J' => Self::Long,
|
||||
'L' => Self::Object({
|
||||
let mut name = String::with_capacity(32); // we can expect ClassNames to be at least this long
|
||||
loop {
|
||||
let char = chars
|
||||
.next()
|
||||
.ok_or_else(|| ParseErr::str("Expected ; before end of string"))?;
|
||||
|
||||
if char == ';' {
|
||||
break;
|
||||
};
|
||||
name.push(char);
|
||||
}
|
||||
name
|
||||
}),
|
||||
'S' => Self::Short,
|
||||
'Z' => Self::Boolean,
|
||||
'[' => Self::Array(Box::new(Self::from_char_iter(chars)?)),
|
||||
c => {
|
||||
return Err(ParseErr::string(format!(
|
||||
"Invalid char in field descriptor {}",
|
||||
c
|
||||
)))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for MethodDescriptor {
|
||||
type Err = ParseErr;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut chars = s.chars().peekable();
|
||||
if chars.next().ok_or_else(|| ParseErr::str("Empty string"))? != '(' {
|
||||
return Err(ParseErr::str("Needs to start with '('"));
|
||||
}
|
||||
|
||||
let mut parameters = Vec::new();
|
||||
|
||||
loop {
|
||||
if let Some(')') = chars.peek() {
|
||||
let _ = chars.next(); // consume the )
|
||||
break;
|
||||
}
|
||||
parameters.push(FieldType::from_char_iter(&mut chars)?);
|
||||
}
|
||||
|
||||
let return_ = if let Some('V') = chars.peek() {
|
||||
MethodType::Void
|
||||
} else {
|
||||
MethodType::Some(FieldType::from_char_iter(&mut chars)?)
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
parameters,
|
||||
return_,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
126
crates/class-struct/src/test.rs
Normal file
126
crates/class-struct/src/test.rs
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn field_descriptor() {
|
||||
let descriptors = [
|
||||
FieldDescriptor::from_str("B").unwrap(),
|
||||
FieldDescriptor::from_str("C").unwrap(),
|
||||
FieldDescriptor::from_str("D").unwrap(),
|
||||
FieldDescriptor::from_str("F").unwrap(),
|
||||
FieldDescriptor::from_str("I").unwrap(),
|
||||
FieldDescriptor::from_str("J").unwrap(),
|
||||
FieldDescriptor::from_str("S").unwrap(),
|
||||
FieldDescriptor::from_str("Z").unwrap(),
|
||||
FieldDescriptor::from_str("[B").unwrap(),
|
||||
FieldDescriptor::from_str("[[Z").unwrap(),
|
||||
FieldDescriptor::from_str("Ljava/lang/String;").unwrap(),
|
||||
FieldDescriptor::from_str("[[[Ljava/lang/String;").unwrap(),
|
||||
];
|
||||
|
||||
type FT = FieldType;
|
||||
|
||||
let expected_descriptors = [
|
||||
FieldDescriptor(FT::Byte),
|
||||
FieldDescriptor(FT::Char),
|
||||
FieldDescriptor(FT::Double),
|
||||
FieldDescriptor(FT::Float),
|
||||
FieldDescriptor(FT::Int),
|
||||
FieldDescriptor(FT::Long),
|
||||
FieldDescriptor(FT::Short),
|
||||
FieldDescriptor(FT::Boolean),
|
||||
FieldDescriptor(FT::Array(Box::new(FT::Byte))),
|
||||
FieldDescriptor(FT::Array(Box::new(FT::Array(Box::new(FT::Boolean))))),
|
||||
FieldDescriptor(FT::Object("java/lang/String".to_string())),
|
||||
FieldDescriptor(FT::Array(Box::new(FT::Array(Box::new(FT::Array(
|
||||
Box::new(FT::Object("java/lang/String".to_string())),
|
||||
)))))),
|
||||
];
|
||||
|
||||
let invalid_descriptors = ["", "Q", "[]", "[", "Ljava/lang/String", "L", "[[[Ljava"];
|
||||
|
||||
descriptors
|
||||
.iter()
|
||||
.zip(expected_descriptors.iter())
|
||||
.for_each(|(a, b)| assert_eq!(a, b));
|
||||
|
||||
invalid_descriptors
|
||||
.iter()
|
||||
.map(|d| FieldDescriptor::from_str(d))
|
||||
.for_each(|rs| {
|
||||
if rs.is_ok() {
|
||||
panic!("Successfully parsed invalid result, {:?}", rs);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn method_descriptor() {
|
||||
let descriptors = vec![
|
||||
MethodDescriptor::from_str("()V").unwrap(),
|
||||
MethodDescriptor::from_str("(B)V").unwrap(),
|
||||
MethodDescriptor::from_str("([ZZ)Ljava/lang/Object;").unwrap(),
|
||||
MethodDescriptor::from_str("(IDLjava/lang/Thread;)Ljava/lang/Object;").unwrap(),
|
||||
MethodDescriptor::from_str("(BBBBBBBBBB)B").unwrap(),
|
||||
MethodDescriptor::from_str("()Z").unwrap(),
|
||||
];
|
||||
|
||||
type FT = FieldType;
|
||||
|
||||
let expected_descriptors = [
|
||||
MethodDescriptor {
|
||||
parameters: vec![],
|
||||
return_: MethodType::Void,
|
||||
},
|
||||
MethodDescriptor {
|
||||
parameters: vec![FT::Byte],
|
||||
return_: MethodType::Void,
|
||||
},
|
||||
MethodDescriptor {
|
||||
parameters: vec![FT::Array(Box::new(FT::Boolean)), FT::Boolean],
|
||||
return_: MethodType::Some(FT::Object("java/lang/Object".to_string())),
|
||||
},
|
||||
MethodDescriptor {
|
||||
parameters: vec![
|
||||
FT::Int,
|
||||
FT::Double,
|
||||
FT::Object("java/lang/Thread".to_string()),
|
||||
],
|
||||
return_: MethodType::Some(FT::Object("java/lang/Object".to_string())),
|
||||
},
|
||||
MethodDescriptor {
|
||||
parameters: vec![
|
||||
FT::Byte,
|
||||
FT::Byte,
|
||||
FT::Byte,
|
||||
FT::Byte,
|
||||
FT::Byte,
|
||||
FT::Byte,
|
||||
FT::Byte,
|
||||
FT::Byte,
|
||||
FT::Byte,
|
||||
FT::Byte,
|
||||
],
|
||||
return_: MethodType::Some(FT::Byte),
|
||||
},
|
||||
MethodDescriptor {
|
||||
parameters: vec![],
|
||||
return_: MethodType::Some(FT::Boolean),
|
||||
},
|
||||
];
|
||||
|
||||
let invalid_descriptors = ["()", "(V)V", ")V", "(;)Z", "(java/lang/StringZ)", "V"];
|
||||
|
||||
invalid_descriptors
|
||||
.iter()
|
||||
.map(|d| MethodDescriptor::from_str(d))
|
||||
.for_each(|rs| {
|
||||
if rs.is_ok() {
|
||||
panic!("Successfully parsed invalid result, {:?}", rs);
|
||||
}
|
||||
});
|
||||
|
||||
descriptors
|
||||
.iter()
|
||||
.zip(expected_descriptors.iter())
|
||||
.for_each(|(a, b)| assert_eq!(a, b));
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue