use gimli::{DebugInfoOffset, UnitOffset};
use itertools::Itertools;
use std::collections::HashMap;
use std::fmt::{Debug, Display, Formatter};
use std::path::PathBuf;
#[derive(PartialEq, Clone)]
pub enum Literal {
String(String),
Int(i64),
Float(f64),
Address(usize),
Bool(bool),
EnumVariant(String, Option<Box<Literal>>),
Array(Box<[LiteralOrWildcard]>),
AssocArray(HashMap<String, LiteralOrWildcard>),
}
impl Display for Literal {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Literal::String(str) => f.write_fmt(format_args!("\"{str}\"")),
Literal::Int(i) => f.write_str(&i.to_string()),
Literal::Float(float) => f.write_str(&float.to_string()),
Literal::Address(addr) => f.write_fmt(format_args!("{addr:#016X}")),
Literal::Bool(b) => f.write_fmt(format_args!("{b}")),
Literal::EnumVariant(variant, data) => {
if let Some(data) = data {
f.write_fmt(format_args!("{variant}({data})"))
} else {
f.write_fmt(format_args!("{variant}"))
}
}
Literal::Array(array) => {
let body = array
.iter()
.map(|item| match item {
LiteralOrWildcard::Literal(lit) => lit.to_string(),
LiteralOrWildcard::Wildcard => "*".to_string(),
})
.join(", ");
f.write_fmt(format_args!("{{ {body} }}"))
}
Literal::AssocArray(assoc_array) => {
let body = assoc_array
.iter()
.map(|(key, value)| {
let value_string = match value {
LiteralOrWildcard::Literal(lit) => lit.to_string(),
LiteralOrWildcard::Wildcard => "*".to_string(),
};
format!("\"{key}\": {value_string}")
})
.join(", ");
f.write_fmt(format_args!("{{ {body} }}"))
}
}
}
}
impl Debug for Literal {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.to_string())
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum LiteralOrWildcard {
Literal(Literal),
Wildcard,
}
macro_rules! impl_equal {
($lhs: expr, $rhs: expr, $lit: path) => {
if let $lit(lhs) = $lhs {
lhs == &$rhs
} else {
false
}
};
}
impl Literal {
pub fn equal_with_string(&self, rhs: &str) -> bool {
impl_equal!(self, rhs, Literal::String)
}
pub fn equal_with_address(&self, rhs: usize) -> bool {
impl_equal!(self, rhs, Literal::Address)
}
pub fn equal_with_bool(&self, rhs: bool) -> bool {
impl_equal!(self, rhs, Literal::Bool)
}
pub fn equal_with_int(&self, rhs: i64) -> bool {
impl_equal!(self, rhs, Literal::Int)
}
pub fn equal_with_float(&self, rhs: f64) -> bool {
const EPS: f64 = 0.0000001f64;
if let Literal::Float(float) = self {
let diff = (*float - rhs).abs();
diff < EPS
} else {
false
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum Selector {
Name { var_name: String, local_only: bool },
Any,
}
impl Selector {
pub fn by_name(name: impl ToString, local_only: bool) -> Self {
Self::Name {
var_name: name.to_string(),
local_only,
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct PointerCast {
pub ptr: usize,
pub ty: String,
}
impl PointerCast {
pub fn new(ptr: usize, ty: impl ToString) -> Self {
Self {
ptr,
ty: ty.to_string(),
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct DataCast {
pub ptr: usize,
pub ty_debug_info: PathBuf,
pub ty_unit_off: DebugInfoOffset,
pub ty_die_off: UnitOffset,
}
impl DataCast {
pub fn new(
ptr: usize,
type_debug_info: impl Into<PathBuf>,
type_unit_off: DebugInfoOffset,
type_die_off: UnitOffset,
) -> Self {
Self {
ptr,
ty_debug_info: type_debug_info.into(),
ty_unit_off: type_unit_off,
ty_die_off: type_die_off,
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum Dqe {
Variable(Selector),
PtrCast(PointerCast),
Field(Box<Dqe>, String),
Index(Box<Dqe>, Literal),
Slice(Box<Dqe>, Option<usize>, Option<usize>),
Deref(Box<Dqe>),
Address(Box<Dqe>),
Canonic(Box<Dqe>),
DataCast(DataCast),
}
impl Dqe {
pub fn boxed(self) -> Box<Self> {
Box::new(self)
}
}
#[cfg(test)]
mod test {
use super::*;
use std::collections::HashMap;
#[test]
fn test_literal_display() {
struct TestCase {
literal: Literal,
expect: &'static str,
}
let test_cases = &[
TestCase {
literal: Literal::String("abc".to_string()),
expect: "\"abc\"",
},
TestCase {
literal: Literal::Array(Box::new([
LiteralOrWildcard::Literal(Literal::Int(1)),
LiteralOrWildcard::Literal(Literal::Int(1)),
LiteralOrWildcard::Wildcard,
])),
expect: "{ 1, 1, * }",
},
TestCase {
literal: Literal::Address(101),
expect: "0x00000000000065",
},
TestCase {
literal: Literal::EnumVariant(
"Some".to_string(),
Some(Box::new(Literal::Array(Box::new([
LiteralOrWildcard::Literal(Literal::Bool(true)),
])))),
),
expect: "Some({ true })",
},
TestCase {
literal: Literal::AssocArray(HashMap::from([(
"__1".to_string(),
LiteralOrWildcard::Literal(Literal::String("abc".to_string())),
)])),
expect: "{ \"__1\": \"abc\" }",
},
];
for tc in test_cases {
assert_eq!(tc.literal.to_string(), tc.expect);
}
}
}