use std::convert::TryFrom;
use llvm_support::{
DllStorageClass, Linkage, RuntimePreemption, ThreadLocalMode, Type, UnnamedAddr, Visibility,
};
use num_enum::TryFromPrimitiveError;
use thiserror::Error;
use crate::map::{CtxMappable, MapCtx};
use crate::record::StrtabError;
use crate::unroll::UnrolledRecord;
#[derive(Debug, Error)]
pub enum AliasError {
#[error("alias record too short: {0} < 5 fields")]
TooShort(usize),
#[error("unsupported alias record format (v1)")]
V1Unsupported,
#[error("error while accessing string table")]
Strtab(#[from] StrtabError),
#[error("invalid type table index: {0}")]
Type(u64),
#[error("invalid visibility")]
Visibility(#[from] TryFromPrimitiveError<Visibility>),
#[error("invalid storage class")]
DllStorageClass(#[from] TryFromPrimitiveError<DllStorageClass>),
}
#[derive(Debug)]
pub struct Alias<'ctx> {
pub name: &'ctx str,
pub ty: &'ctx Type,
pub value_index: u64,
pub linkage: Linkage,
pub visibility: Visibility,
pub storage_class: DllStorageClass,
pub tls_mode: ThreadLocalMode,
pub unnamed_addr: UnnamedAddr,
pub preemption_specifier: RuntimePreemption,
}
impl<'ctx> CtxMappable<'ctx, UnrolledRecord> for Alias<'ctx> {
type Error = AliasError;
fn try_map(record: &UnrolledRecord, ctx: &'ctx MapCtx) -> Result<Self, Self::Error> {
let fields = record.fields();
if !ctx.use_strtab() {
return Err(AliasError::V1Unsupported);
}
if fields.len() < 5 {
return Err(AliasError::TooShort(fields.len()));
}
let name = ctx.strtab.read_name(record)?;
let ty = ctx
.type_table
.get(fields[2])
.ok_or(AliasError::Type(fields[2]))?;
let value_index = fields[3];
let linkage = Linkage::from(fields[4]);
let visibility = fields
.get(5)
.map_or_else(|| Ok(Visibility::Default), |v| Visibility::try_from(*v))?;
let storage_class = fields.get(6).map_or_else(
|| Ok(DllStorageClass::Default),
|v| DllStorageClass::try_from(*v),
)?;
let tls_mode = fields
.get(7)
.copied()
.map(ThreadLocalMode::from)
.unwrap_or(ThreadLocalMode::NotThreadLocal);
let unnamed_addr = fields
.get(8)
.copied()
.map(UnnamedAddr::from)
.unwrap_or(UnnamedAddr::None);
let preemption_specifier = fields
.get(9)
.copied()
.map(RuntimePreemption::from)
.unwrap_or(RuntimePreemption::DsoPreemptable);
Ok(Alias {
name,
ty,
value_index,
linkage,
visibility,
storage_class,
tls_mode,
unnamed_addr,
preemption_specifier,
})
}
}