use crate::{
error::Error,
opcode::{Opcode, parse_opcode},
reader::Reader,
ty::Type,
};
#[derive(Clone, Debug, PartialEq)]
pub struct Instruction<'a> {
pub offset: u32,
pub opcode: Opcode<'a>,
pub next_offset: u32,
}
#[derive(Clone, Debug)]
pub struct ProcDisasm<'a> {
pub proc_index: u32,
pub bytecode_offset: u32,
pub instructions: Vec<Instruction<'a>>,
}
pub(crate) fn disassemble_proc<'a>(
blob: &'a [u8],
proc_index: u32,
bytecode_offset: u32,
bytecode_len: u32,
types: &[Type<'a>],
) -> Result<ProcDisasm<'a>, Error> {
let start = bytecode_offset as usize;
let end = (bytecode_offset as u64)
.checked_add(u64::from(bytecode_len))
.ok_or(Error::Overflow {
what: "bytecode end offset",
})?;
let end = usize::try_from(end).map_err(|_| Error::Overflow {
what: "bytecode end offset",
})?;
let body = blob.get(start..end).ok_or(Error::BytecodeOutOfRange {
offset: bytecode_offset,
length: bytecode_len,
})?;
let mut reader = Reader::new(body);
let mut instructions = Vec::new();
while reader.pos() < reader.len() {
let local_offset = reader.pos();
let opcode = parse_opcode(&mut reader, types)?;
let absolute_offset = bytecode_offset
.checked_add(u32::try_from(local_offset).unwrap_or(u32::MAX))
.ok_or(Error::Overflow {
what: "instruction absolute offset",
})?;
let next_offset = bytecode_offset
.checked_add(u32::try_from(reader.pos()).unwrap_or(u32::MAX))
.ok_or(Error::Overflow {
what: "instruction next offset",
})?;
instructions.push(Instruction {
offset: absolute_offset,
opcode,
next_offset,
});
}
Ok(ProcDisasm {
proc_index,
bytecode_offset,
instructions,
})
}