use super::super::super::clr::{argument::Argument, local::Local, token::Token};
use super::super::instruction::{Instruction, Operand};
use super::super::{enums::*, opcode::*};
use crate::{Result, error::Error};
use byteorder::ReadBytesExt;
use std::io::{Cursor, Seek};
pub struct Reader<'a> {
stream: Cursor<&'a [u8]>,
}
impl<'a> Reader<'a> {
pub fn new(bytes: &'a [u8]) -> Self {
Self {
stream: Cursor::new(bytes),
}
}
pub fn tell(&mut self) -> Result<usize> {
Ok(self.stream.stream_position()? as usize)
}
pub fn seek(&mut self, pos: usize) -> Result<usize> {
Ok(self.stream.seek(std::io::SeekFrom::Start(pos as u64))? as usize)
}
#[allow(clippy::unused_self)]
pub fn is_arg_operand_instruction(&self, insn: &Instruction) -> bool {
[
OpCodeValue::Ldarg,
OpCodeValue::Ldarg_S,
OpCodeValue::Ldarga,
OpCodeValue::Ldarga_S,
OpCodeValue::Starg,
OpCodeValue::Starg_S,
]
.contains(&insn.opcode.value)
}
pub fn read_u8(&mut self) -> Result<u8> {
Ok(self.stream.read_u8()?)
}
pub fn read_i8(&mut self) -> Result<i8> {
Ok(self.stream.read_i8()?)
}
pub fn read_u16(&mut self) -> Result<u16> {
Ok(self.stream.read_u16::<byteorder::LittleEndian>()?)
}
pub fn read_i16(&mut self) -> Result<i16> {
Ok(self.stream.read_i16::<byteorder::LittleEndian>()?)
}
pub fn read_u32(&mut self) -> Result<u32> {
Ok(self.stream.read_u32::<byteorder::LittleEndian>()?)
}
pub fn read_i32(&mut self) -> Result<i32> {
Ok(self.stream.read_i32::<byteorder::LittleEndian>()?)
}
pub fn read_u64(&mut self) -> Result<u64> {
Ok(self.stream.read_u64::<byteorder::LittleEndian>()?)
}
pub fn read_i64(&mut self) -> Result<i64> {
Ok(self.stream.read_i64::<byteorder::LittleEndian>()?)
}
pub fn read_f32(&mut self) -> Result<f32> {
Ok(self.stream.read_f32::<byteorder::LittleEndian>()?)
}
pub fn read_f64(&mut self) -> Result<f64> {
Ok(self.stream.read_f64::<byteorder::LittleEndian>()?)
}
pub fn read_inline_br_target(&mut self, insn: &Instruction) -> Result<Operand> {
let branch_offset = self.read_u32()? as usize;
Ok(Operand::Int(
(insn.offset + insn.size() + branch_offset) as i64,
))
}
pub fn read_inline_field(&mut self, _insn: &Instruction) -> Result<Operand> {
let token_value = self.read_u32()? as usize;
Ok(Operand::Token(Token::new(token_value)))
}
pub fn read_inline_i(&mut self, _insn: &Instruction) -> Result<Operand> {
let v = self.read_u32()? as i64;
Ok(Operand::Int(v))
}
pub fn read_inline_i8(&mut self, _insn: &Instruction) -> Result<Operand> {
let v = self.read_i64()?;
Ok(Operand::Int(v))
}
pub fn read_inline_method(&mut self, _insn: &Instruction) -> Result<Operand> {
let token_value = self.read_u32()? as usize;
Ok(Operand::Token(Token::new(token_value)))
}
pub fn read_inline_none(&mut self, _insn: &Instruction) -> Result<Operand> {
Ok(Operand::None)
}
pub fn read_inline_phi(&mut self, _insn: &Instruction) -> Result<Operand> {
Ok(Operand::None)
}
pub fn read_inline_r(&mut self, _insn: &Instruction) -> Result<Operand> {
let v = self.read_f64()?;
Ok(Operand::Float(v))
}
pub fn read_inline_sig(&mut self, _insn: &Instruction) -> Result<Operand> {
let token_value = self.read_u32()? as usize;
Ok(Operand::Token(Token::new(token_value)))
}
pub fn read_inline_string(&mut self, _insn: &Instruction) -> Result<Operand> {
let token_value = self.read_u32()? as usize;
Ok(Operand::StringToken(Token::new(token_value)))
}
pub fn read_inline_switch(&mut self, insn: &Instruction) -> Result<Operand> {
let num_branches = self.read_u32()? as usize;
let offset_after_insn = insn.offset + insn.opcode.size() + 4 + num_branches * 4;
let mut branches = vec![];
for _ in 0..num_branches {
let branch_offset = self.read_u32()? as usize;
branches.push(Operand::Int((offset_after_insn + branch_offset) as i64));
}
Ok(Operand::Arguments(branches))
}
pub fn read_inline_tok(&mut self, _insn: &Instruction) -> Result<Operand> {
let token_value = self.read_u32()? as usize;
Ok(Operand::Token(Token::new(token_value)))
}
pub fn read_inline_type(&mut self, _insn: &Instruction) -> Result<Operand> {
let token_value = self.read_u32()? as usize;
Ok(Operand::Token(Token::new(token_value)))
}
pub fn read_inline_var(&mut self, insn: &Instruction) -> Result<Operand> {
let var_value = self.read_u16()?;
if self.is_arg_operand_instruction(insn) {
Ok(Operand::Argument(Argument::new(var_value as usize)))
} else {
Ok(Operand::Local(Local::new(var_value as usize)))
}
}
pub fn read_short_inline_br_target(&mut self, insn: &Instruction) -> Result<Operand> {
let branch_offset = self.read_u8()? as usize;
Ok(Operand::Int(
(insn.offset + insn.size() + branch_offset) as i64,
))
}
pub fn read_short_inline_i(&mut self, insn: &Instruction) -> Result<Operand> {
if insn.opcode.value == OpCodeValue::Ldc_I4_S {
let val = self.read_i8()?;
Ok(Operand::Int(val as i64))
} else {
let val = self.read_u8()?;
Ok(Operand::Int(val as i64))
}
}
pub fn read_short_inline_r(&mut self, _insn: &Instruction) -> Result<Operand> {
Ok(Operand::Float(self.read_f32()? as f64))
}
pub fn read_short_inline_var(&mut self, insn: &Instruction) -> Result<Operand> {
let var_value = self.read_u8()?;
if self.is_arg_operand_instruction(insn) {
Ok(Operand::Argument(Argument::new(var_value as usize)))
} else {
Ok(Operand::Local(Local::new(var_value as usize)))
}
}
pub fn read_instruction(&mut self, off: usize) -> Result<Instruction> {
let mut insn = Instruction::new();
insn.offset = off;
insn.opcode = self.read_opcode()?;
insn.operand = self.read_operand(&insn)?;
Ok(insn)
}
pub fn read_opcode(&mut self) -> Result<OpCode> {
let op_value_first = self.read_u8()? as usize;
if op_value_first == 0xFE {
let op_value_second = self.read_u8()? as usize;
Ok(OPCODES.two_byte_op_codes[op_value_second].clone())
} else {
Ok(OPCODES.one_byte_op_codes[op_value_first].clone())
}
}
#[allow(clippy::too_many_lines)]
pub fn read_operand(&mut self, insn: &Instruction) -> Result<Operand> {
match insn.opcode.operand_type {
OperandType::InlineBrTarget => self.read_inline_br_target(insn),
OperandType::InlineField => self.read_inline_field(insn),
OperandType::InlineI => self.read_inline_i(insn),
OperandType::InlineI8 => self.read_inline_i8(insn),
OperandType::InlineMethod => self.read_inline_method(insn),
OperandType::InlineNone => self.read_inline_none(insn),
OperandType::InlinePhi => self.read_inline_phi(insn),
OperandType::InlineR => self.read_inline_r(insn),
OperandType::InlineSig => self.read_inline_sig(insn),
OperandType::InlineString => self.read_inline_string(insn),
OperandType::InlineSwitch => self.read_inline_switch(insn),
OperandType::InlineTok => self.read_inline_tok(insn),
OperandType::InlineType => self.read_inline_type(insn),
OperandType::InlineVar => self.read_inline_var(insn),
OperandType::ShortInlineBrTarget => self.read_short_inline_br_target(insn),
OperandType::ShortInlineI => self.read_short_inline_i(insn),
OperandType::ShortInlineR => self.read_short_inline_r(insn),
OperandType::ShortInlineVar => self.read_short_inline_var(insn),
_ => Err(Error::UndefinedOperandType(
insn.opcode.operand_type.clone(),
)),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn reader_borrows_input_without_copying() {
let buf = [0x01_u8, 0x02, 0x03, 0x04];
let mut r = Reader::new(&buf);
assert_eq!(r.tell().unwrap(), 0);
assert_eq!(r.read_u8().unwrap(), 0x01);
assert_eq!(r.tell().unwrap(), 1);
}
#[test]
fn reader_read_u16_little_endian() {
let mut r = Reader::new(&[0x34, 0x12]);
assert_eq!(r.read_u16().unwrap(), 0x1234);
}
#[test]
fn reader_read_u32_little_endian() {
let mut r = Reader::new(&[0x78, 0x56, 0x34, 0x12]);
assert_eq!(r.read_u32().unwrap(), 0x1234_5678);
}
#[test]
fn reader_read_i32_negative() {
let mut r = Reader::new(&[0xFF, 0xFF, 0xFF, 0xFF]);
assert_eq!(r.read_i32().unwrap(), -1_i32);
}
#[test]
fn reader_seek_and_tell() {
let mut r = Reader::new(&[0_u8; 16]);
r.seek(8).unwrap();
assert_eq!(r.tell().unwrap(), 8);
r.seek(0).unwrap();
assert_eq!(r.tell().unwrap(), 0);
}
#[test]
fn reader_read_past_end_errors() {
let mut r = Reader::new(&[]);
assert!(r.read_u8().is_err());
}
}