use super::children::for_each_child;
use super::primitives::entry_type;
use super::{from_fn, Parser};
use crate::parser::combinators::all;
use crate::parser::primitives::{is_member_tag, name};
use crate::{Die, DwarfDb};
#[derive(Debug, Clone)]
pub struct FunctionInfo<'db> {
pub name: String,
pub linkage_name: Option<String>,
pub die: Die<'db>,
pub parameters: Vec<ParameterInfo<'db>>,
pub return_type: Option<Die<'db>>,
}
#[derive(Debug, Clone)]
pub struct ParameterInfo<'db> {
pub name: Option<String>,
pub type_die: Die<'db>,
}
pub fn function_parser<'db>() -> impl Parser<'db, Option<FunctionInfo<'db>>> {
from_fn(
|db: &'db dyn DwarfDb, entry: Die<'db>| -> anyhow::Result<Option<FunctionInfo<'db>>> {
if entry.tag(db) != crate::gimli::DW_TAG_subprogram {
return Ok(None);
}
let name = entry.name(db).unwrap_or_else(|_| "<anonymous>".to_string());
let linkage_name = entry.string_attr(db, crate::gimli::DW_AT_linkage_name).ok();
let return_type = entry_type().parse(db, entry).ok();
let parameters = parameter_list_parser().parse(db, entry)?;
Ok(Some(FunctionInfo {
name,
linkage_name,
die: entry,
parameters,
return_type,
}))
},
)
}
fn parameter_parser<'db>() -> impl Parser<'db, Option<ParameterInfo<'db>>> {
from_fn(
|db: &'db dyn DwarfDb, entry: Die<'db>| -> anyhow::Result<Option<ParameterInfo<'db>>> {
if entry.tag(db) != crate::gimli::DW_TAG_formal_parameter {
return Ok(None);
}
let name = entry.name(db).ok();
let type_die = entry_type().parse(db, entry)?;
Ok(Some(ParameterInfo { name, type_die }))
},
)
}
fn parameter_list_parser<'db>() -> impl Parser<'db, Vec<ParameterInfo<'db>>> {
for_each_child(parameter_parser()).map(|results| results.into_iter().flatten().collect())
}
pub fn child_functions_parser<'db>() -> impl Parser<'db, Vec<FunctionInfo<'db>>> {
for_each_child(function_parser()).map(|results| results.into_iter().flatten().collect())
}
pub fn impl_namespaces_in_module_parser<'db>() -> impl Parser<'db, Vec<Die<'db>>> {
from_fn(
|db: &'db dyn DwarfDb, type_die: Die<'db>| -> anyhow::Result<Vec<Die<'db>>> {
let mut impl_namespaces = Vec::new();
find_sibling_impl_namespaces(db, type_die, &mut impl_namespaces)?;
tracing::debug!(
"Found {} impl namespaces as siblings to target type",
impl_namespaces.len()
);
Ok(impl_namespaces)
},
)
}
fn find_sibling_impl_namespaces<'db>(
db: &'db dyn DwarfDb,
type_die: Die<'db>,
impl_namespaces: &mut Vec<Die<'db>>,
) -> anyhow::Result<()> {
let module_index = crate::modules::module_index(db, type_die.file(db));
let target_offset = type_die.offset(db);
let Some(module_range) = module_index.find_by_offset(db, target_offset) else {
tracing::debug!("No module range found for offset {target_offset:#x}");
return Ok(());
};
tracing::debug!(
"Target type is in module path: {:?} and DIE: {}",
module_range.module_path,
module_range.die.print(db)
);
let impls = for_each_child(
all((
is_member_tag(gimli::DW_TAG_namespace),
name()
.map_with_db_and_entry(|_, entry, n| {
n.and_then(|n| {
if n.starts_with("{impl#") && n.ends_with('}') {
tracing::debug!("Found impl namespace: {n}");
Some(entry)
} else {
None
}
})
})
.map_res(|entry| {
entry.ok_or_else(|| anyhow::anyhow!("Expected impl namespace DIE, found None"))
}),
))
.map(|(die, _)| die),
)
.parse(db, module_range.die)?;
impl_namespaces.extend(impls);
Ok(())
}