use anyhow::Result;
use rudy_types::{
CEnumLayout, CEnumVariant, Discriminant, DiscriminantType, EnumLayout, EnumVariantLayout,
Layout, PrimitiveLayout,
};
use crate::{
parser::{
children::{for_each_child, parse_children},
combinators::all,
primitives::{
attr, data_offset, entry_type, is_member_tag, member, member_by_tag, optional_attr,
resolve_type_shallow, IsMember,
},
Parser,
},
types::resolve_entry_type,
Die, DwarfDb,
};
pub fn enum_discriminant<'db>() -> impl Parser<'db, Discriminant> {
struct DiscriminantParser;
impl<'db> Parser<'db, Discriminant> for DiscriminantParser {
fn parse(&self, db: &'db dyn DwarfDb, variants_entry: Die<'db>) -> Result<Discriminant> {
let (discr_die, offset) = optional_attr::<Die<'db>>(gimli::DW_AT_discr)
.and(optional_attr::<usize>(gimli::DW_AT_data_member_location))
.parse(db, variants_entry)?;
if let Some(discr) = discr_die {
let discriminant_type = resolve_entry_type(db, discr)?;
let ty = match discriminant_type.layout.as_ref() {
rudy_types::Layout::Primitive(rudy_types::PrimitiveLayout::Int(i)) => {
DiscriminantType::Int(*i)
}
rudy_types::Layout::Primitive(rudy_types::PrimitiveLayout::UnsignedInt(u)) => {
DiscriminantType::UnsignedInt(*u)
}
_ => {
tracing::warn!(
"discriminant type is not an integer: {discriminant_type:?} {}",
discr.location(db)
);
DiscriminantType::Implicit
}
};
Ok(Discriminant {
ty,
offset: offset.unwrap_or(0),
})
} else {
Ok(Discriminant {
ty: DiscriminantType::Implicit,
offset: 0,
})
}
}
}
DiscriminantParser
}
pub fn enum_variant<'db>() -> impl Parser<'db, EnumVariantLayout<Die<'db>>> {
is_member_tag(gimli::DW_TAG_variant)
.then(
optional_attr::<i128>(gimli::DW_AT_discr_value).and(
member_by_tag(gimli::DW_TAG_member).then(
all((
attr::<String>(gimli::DW_AT_name),
data_offset(),
entry_type().then(resolve_type_shallow()),
)),
),
),
)
.map(|(discriminant, (name, offset, layout))| {
debug_assert_eq!(offset, 0, "enum variants should not have offsets");
EnumVariantLayout {
name,
discriminant,
layout,
}
})
}
pub struct EnumNamedTupleVariant<T> {
variant_name: String,
parser: T,
}
pub fn enum_named_tuple_variant<T>(variant_name: &str, parser: T) -> EnumNamedTupleVariant<T> {
let variant_name = variant_name.to_string();
EnumNamedTupleVariant {
variant_name,
parser,
}
}
pub(super) struct PartiallyParsedEnumVariant<'db> {
pub discriminant: Option<usize>,
pub offset: usize,
pub layout: Die<'db>,
}
pub(super) fn named_enum_variant<'db>(
variant_name: &str,
) -> impl Parser<'db, PartiallyParsedEnumVariant<'db>> {
is_member_tag(gimli::DW_TAG_variant).then(
optional_attr::<usize>(gimli::DW_AT_discr)
.and(member(variant_name).then(all((data_offset(), entry_type()))))
.map(
|(discriminant, (offset, layout))| PartiallyParsedEnumVariant {
discriminant,
offset,
layout,
},
),
)
}
macro_rules! impl_parse_enum_named_tuple_variant_for_tuples {
(
$($P:ident, $T:ident, $idx:tt),*
) => {
impl<'db, $($T, $P,)*> Parser<'db, (Option<usize>, ($((usize, $T),)*))> for EnumNamedTupleVariant<($($P,)*)>
where
$($P: Parser<'db, $T>),*
{
fn parse(&self, db: &'db dyn DwarfDb, entry: Die<'db>) -> anyhow::Result<(Option<usize>, ($((usize, $T),)*))> {
let variant = named_enum_variant(&self.variant_name).parse(db, entry)?;
debug_assert_eq!(variant.offset, 0, "enum variants should not have offsets");
let field_parser = (
$(
IsMember { expected_name: format!("__{}", $idx) }
.then(all((
data_offset(),
entry_type().then(&self.parser.$idx)
))),
)*
);
parse_children(field_parser).map(|fields| {
let discriminant = variant.discriminant;
(discriminant, fields)
}).parse(db, variant.layout)
}
}
};
}
impl_parse_enum_named_tuple_variant_for_tuples!();
impl_parse_enum_named_tuple_variant_for_tuples!(P0, T0, 0);
impl_parse_enum_named_tuple_variant_for_tuples!(P0, T0, 0, P1, T1, 1);
impl_parse_enum_named_tuple_variant_for_tuples!(P0, T0, 0, P1, T1, 1, P2, T2, 2);
impl_parse_enum_named_tuple_variant_for_tuples!(P0, T0, 0, P1, T1, 1, P2, T2, 2, P3, T3, 3);
impl_parse_enum_named_tuple_variant_for_tuples!(
P0, T0, 0, P1, T1, 1, P2, T2, 2, P3, T3, 3, P4, T4, 4
);
impl_parse_enum_named_tuple_variant_for_tuples!(
P0, T0, 0, P1, T1, 1, P2, T2, 2, P3, T3, 3, P4, T4, 4, P5, T5, 5
);
impl_parse_enum_named_tuple_variant_for_tuples!(
P0, T0, 0, P1, T1, 1, P2, T2, 2, P3, T3, 3, P4, T4, 4, P5, T5, 5, P6, T6, 6
);
impl_parse_enum_named_tuple_variant_for_tuples!(
P0, T0, 0, P1, T1, 1, P2, T2, 2, P3, T3, 3, P4, T4, 4, P5, T5, 5, P6, T6, 6, P7, T7, 7
);
pub fn enum_def<'db>() -> impl Parser<'db, EnumLayout<Die<'db>>> {
EnumParser
}
pub struct EnumParser;
impl<'db> Parser<'db, EnumLayout<Die<'db>>> for EnumParser {
fn parse(&self, db: &'db dyn DwarfDb, entry: Die<'db>) -> Result<EnumLayout<Die<'db>>> {
tracing::debug!("resolving enum type: {}", entry.print(db));
let (name, size, variants_entry) = all((
attr::<String>(gimli::DW_AT_name),
attr::<usize>(gimli::DW_AT_byte_size),
member_by_tag(gimli::DW_TAG_variant_part),
))
.parse(db, entry)?;
let discriminant = enum_discriminant().parse(db, variants_entry)?;
let variants = for_each_child(enum_variant()).parse(db, variants_entry)?;
Ok(EnumLayout {
name,
variants,
size,
discriminant,
})
}
}
pub fn c_enum_variant<'db>() -> impl Parser<'db, CEnumVariant> {
all((
attr::<String>(gimli::DW_AT_name),
attr::<i128>(gimli::DW_AT_const_value),
))
.map(|(name, value)| CEnumVariant { name, value })
}
pub fn c_enum_def<'db>() -> impl Parser<'db, CEnumLayout> {
struct CEnumParser;
impl<'db> Parser<'db, CEnumLayout> for CEnumParser {
fn parse(&self, db: &'db dyn DwarfDb, entry: Die<'db>) -> Result<CEnumLayout> {
tracing::debug!("resolving C-style enum type: {}", entry.print(db));
let (name, size, underlying_type) = all((
attr::<String>(gimli::DW_AT_name),
attr::<usize>(gimli::DW_AT_byte_size),
entry_type()
.then(resolve_type_shallow())
.map_res(|ty| match ty.layout.as_ref() {
Layout::Primitive(PrimitiveLayout::Int(i)) => Ok(DiscriminantType::Int(*i)),
Layout::Primitive(PrimitiveLayout::UnsignedInt(u)) => {
Ok(DiscriminantType::UnsignedInt(*u))
}
_ => Err(anyhow::anyhow!(
"C enum underlying type must be integer, got: {:?}",
ty
)),
}),
))
.parse(db, entry)?;
let variants = for_each_child(c_enum_variant()).parse(db, entry)?;
Ok(CEnumLayout {
name,
discriminant_type: underlying_type,
variants,
size,
})
}
}
CEnumParser
}