use crate::{
attribute,
bytecode::{ProcDisasm, disassemble_proc},
disasm::{ContainerSummary, DisasmDisplay},
error::Error,
header::{HEADER_SIZE, Header},
proc::{Proc, ProcKind, has_attributes, parse_proc},
reader::Reader,
ty::{Type, parse_type},
var::{Var, parse_var},
};
#[derive(Clone, Debug)]
pub struct Container<'a> {
bytes: &'a [u8],
header: Header,
types: Vec<Type<'a>>,
procs: Vec<Proc<'a>>,
vars: Vec<Var<'a>>,
}
impl<'a> Container<'a> {
pub fn parse(bytes: &'a [u8]) -> Result<Self, Error> {
let header = Header::parse(bytes)?;
let mut reader = Reader::new(bytes);
reader.skip(HEADER_SIZE, "header skip")?;
let mut types: Vec<Type<'a>> = Vec::with_capacity(header.type_count as usize);
for _ in 0..header.type_count {
let so_far = u32::try_from(types.len()).unwrap_or(u32::MAX);
types.push(parse_type(&mut reader, so_far)?);
if header.build_no >= 21 {
let attrs = attribute::parse_block(&mut reader, &types)?;
if let Some(last) = types.last_mut() {
last.attributes = attrs;
}
}
}
let mut procs: Vec<Proc<'a>> = Vec::with_capacity(header.proc_count as usize);
for _ in 0..header.proc_count {
let proc = parse_proc(&mut reader, bytes.len())?;
let needs_attrs = has_attributes(proc.flags_raw);
procs.push(proc);
if needs_attrs {
let attrs = attribute::parse_block(&mut reader, &types)?;
if let Some(last) = procs.last_mut() {
last.attributes = attrs;
}
}
}
let mut vars: Vec<Var<'a>> = Vec::with_capacity(header.var_count as usize);
for _ in 0..header.var_count {
vars.push(parse_var(&mut reader, header.type_count)?);
}
if !header.has_no_main_proc() && (header.main_proc_no as usize) >= procs.len() {
return Err(Error::Overflow {
what: "MainProcNo references missing proc",
});
}
Ok(Self {
bytes,
header,
types,
procs,
vars,
})
}
pub fn header(&self) -> Header {
self.header
}
pub fn bytes(&self) -> &'a [u8] {
self.bytes
}
pub fn types(&self) -> &[Type<'a>] {
&self.types
}
pub fn procs(&self) -> &[Proc<'a>] {
&self.procs
}
pub fn vars(&self) -> &[Var<'a>] {
&self.vars
}
pub fn main_proc(&self) -> Option<&Proc<'a>> {
if self.header.has_no_main_proc() {
return None;
}
self.procs.get(self.header.main_proc_no as usize)
}
pub fn display<'c>(&'c self, disasm: &'c ProcDisasm<'a>) -> DisasmDisplay<'a, 'c> {
DisasmDisplay::new(self, disasm)
}
pub fn display_summary(&self) -> ContainerSummary<'a, '_> {
ContainerSummary::new(self)
}
pub fn disassemble(&self, proc_index: u32) -> Result<Option<ProcDisasm<'a>>, Error> {
let count = u32::try_from(self.procs.len()).unwrap_or(u32::MAX);
let proc = self
.procs
.get(proc_index as usize)
.ok_or(Error::TypeIndexOutOfRange {
index: proc_index,
count,
})?;
let internal = match &proc.kind {
ProcKind::Internal(int) => int,
ProcKind::External(_) => return Ok(None),
};
let disasm = disassemble_proc(
self.bytes,
proc_index,
internal.bytecode_offset,
internal.bytecode_len,
&self.types,
)?;
Ok(Some(disasm))
}
}