use core::ops::Deref;
use revm::bytecode::Bytecode;
#[derive(Debug)]
pub struct ExtBytecode {
base: Bytecode,
instruction_pointer: *const u8,
}
impl Deref for ExtBytecode {
type Target = Bytecode;
fn deref(&self) -> &Self::Target {
&self.base
}
}
impl Default for ExtBytecode {
fn default() -> Self {
Self::new(Bytecode::default())
}
}
impl ExtBytecode {
pub fn new(base: Bytecode) -> Self {
let instruction_pointer = base.bytecode_ptr();
Self { base, instruction_pointer }
}
pub fn bytecode_slice(&self) -> &[u8] {
self.base.original_byte_slice()
}
pub fn relative_jump(&mut self, offset: isize) {
debug_assert!(
{
let bytes = self.base.bytes_ref();
let new_pc = self.pc().wrapping_add_signed(offset);
new_pc <= bytes.len()
},
"relative_jump would move instruction pointer out of bounds"
);
self.instruction_pointer = unsafe { self.instruction_pointer.offset(offset) };
}
pub fn absolute_jump(&mut self, offset: usize) {
debug_assert!(
offset <= self.base.bytes_ref().len(),
"absolute_jump would move instruction pointer out of bounds"
);
self.instruction_pointer = unsafe { self.base.bytes_ref().as_ptr().add(offset) };
}
pub fn is_valid_legacy_jump(&mut self, offset: usize) -> bool {
self.base.legacy_jump_table().expect("Panic if not legacy").is_valid(offset)
}
pub fn pc(&self) -> usize {
unsafe { self.instruction_pointer.offset_from_unsigned(self.base.bytes_ref().as_ptr()) }
}
pub fn opcode(&self) -> u8 {
unsafe { *self.instruction_pointer }
}
pub fn read_slice(&self, len: usize) -> &[u8] {
debug_assert!(
{
let bytes = self.base.bytes_ref();
let pc = self.pc();
pc.checked_add(len).map_or(false, |end| end <= bytes.len())
},
"read_slice would read out of bounds"
);
unsafe { core::slice::from_raw_parts(self.instruction_pointer, len) }
}
}