use std::convert::TryFrom;
use llvm_support::{
AlignError, CallingConvention, DllStorageClass, Linkage, MaybeAlign, Type, UnnamedAddr,
Visibility,
};
use num_enum::TryFromPrimitiveError;
use thiserror::Error;
use crate::block::attributes::AttributeEntry;
use crate::map::{CtxMappable, MapCtx};
use crate::record::StrtabError;
use crate::unroll::UnrolledRecord;
#[derive(Debug, Error)]
pub enum FunctionError {
#[error("function record too short: {0} < 10 fields")]
TooShort(usize),
#[error("unsupported function record format (v1)")]
V1Unsupported,
#[error("error while accessing string table")]
Strtab(#[from] StrtabError),
#[error("unknown calling convention")]
CallingConvention(#[from] TryFromPrimitiveError<CallingConvention>),
#[error("invalid type table index: {0}")]
Type(u64),
#[error("invalid attribute entry ID: {0}")]
Attribute(u64),
#[error("invalid alignment")]
Alignment(#[from] AlignError),
#[error("invalid section table index: {0}")]
Section(usize),
#[error("invalid visibility")]
Visibility(#[from] TryFromPrimitiveError<Visibility>),
#[error("invalid GC table index: {0}")]
Gc(usize),
#[error("invalid storage class")]
DllStorageClass(#[from] TryFromPrimitiveError<DllStorageClass>),
}
#[non_exhaustive]
#[derive(Debug)]
pub struct Function<'ctx> {
pub name: &'ctx str,
pub ty: &'ctx Type,
pub calling_convention: CallingConvention,
pub is_declaration: bool,
pub linkage: Linkage,
pub attributes: Option<&'ctx AttributeEntry>,
pub alignment: MaybeAlign,
pub section: Option<&'ctx str>,
pub visibility: Visibility,
pub gc_name: Option<&'ctx str>,
pub unnamed_addr: UnnamedAddr,
pub storage_class: DllStorageClass,
}
impl<'ctx> CtxMappable<'ctx, UnrolledRecord> for Function<'ctx> {
type Error = FunctionError;
fn try_map(record: &UnrolledRecord, ctx: &'ctx MapCtx) -> Result<Self, Self::Error> {
let fields = record.fields();
if !ctx.use_strtab() {
return Err(FunctionError::V1Unsupported);
}
if fields.len() < 10 {
return Err(FunctionError::TooShort(fields.len()));
}
let name = ctx.strtab.read_name(record)?;
let ty = ctx
.type_table
.get(fields[2])
.ok_or(FunctionError::Type(fields[2]))?;
let calling_convention = CallingConvention::try_from(fields[3])?;
let is_declaration = fields[4] != 0;
let linkage = Linkage::from(fields[5]);
let attributes = {
let paramattr = fields[6];
if paramattr == 0 {
None
} else {
Some(
ctx.attributes
.get(paramattr - 1)
.ok_or(FunctionError::Attribute(paramattr))?,
)
}
};
let alignment = MaybeAlign::try_from(fields[7] as u8)?;
let section = match fields[8] as usize {
0 => None,
idx => Some(
ctx.section_table
.get(idx - 1)
.map(AsRef::as_ref)
.ok_or(FunctionError::Section(idx - 1))?,
),
};
let visibility = Visibility::try_from(fields[9])?;
let gc_name = fields
.get(10)
.and_then(|idx| match *idx as usize {
0 => None,
idx => Some(
ctx.gc_table
.get(idx - 1)
.map(AsRef::as_ref)
.ok_or(FunctionError::Gc(idx - 1)),
),
})
.transpose()?;
let unnamed_addr = fields
.get(11)
.copied()
.map(UnnamedAddr::from)
.unwrap_or(UnnamedAddr::None);
let storage_class = fields.get(13).map_or_else(
|| Ok(DllStorageClass::Default),
|v| DllStorageClass::try_from(*v),
)?;
Ok(Self {
name,
ty,
calling_convention,
is_declaration,
linkage,
attributes,
alignment,
section,
visibility,
gc_name,
unnamed_addr,
storage_class,
})
}
}