use super::{Immediates, Jumps, LegacyBytecode};
use crate::{interpreter_types::LoopControl, InterpreterAction};
use bytecode::{utils::read_u16, Bytecode};
use core::ops::Deref;
use primitives::B256;
#[cfg(feature = "serde")]
mod serde;
#[derive(Debug)]
pub struct ExtBytecode {
instruction_pointer: *const u8,
continue_execution: bool,
bytecode_hash: Option<B256>,
pub action: Option<InterpreterAction>,
base: Bytecode,
}
impl Deref for ExtBytecode {
type Target = Bytecode;
fn deref(&self) -> &Self::Target {
&self.base
}
}
impl Default for ExtBytecode {
#[inline]
fn default() -> Self {
Self::new(Bytecode::default())
}
}
impl ExtBytecode {
#[inline]
pub fn new(base: Bytecode) -> Self {
Self::new_with_optional_hash(base, None)
}
#[inline]
pub fn new_with_hash(base: Bytecode, hash: B256) -> Self {
Self::new_with_optional_hash(base, Some(hash))
}
#[inline]
pub fn new_with_optional_hash(base: Bytecode, hash: Option<B256>) -> Self {
let instruction_pointer = base.bytecode_ptr();
Self {
base,
instruction_pointer,
bytecode_hash: hash,
action: None,
continue_execution: true,
}
}
#[inline]
pub fn calculate_hash(&mut self) -> B256 {
let hash = self.base.hash_slow();
self.bytecode_hash = Some(hash);
hash
}
#[inline]
pub fn hash(&mut self) -> Option<B256> {
self.bytecode_hash
}
#[inline]
pub fn get_or_calculate_hash(&mut self) -> B256 {
*self.bytecode_hash.get_or_insert_with(
#[cold]
|| self.base.hash_slow(),
)
}
}
impl LoopControl for ExtBytecode {
#[inline]
fn is_not_end(&self) -> bool {
self.continue_execution
}
#[inline]
fn reset_action(&mut self) {
self.continue_execution = true;
}
#[inline]
fn set_action(&mut self, action: InterpreterAction) {
debug_assert_eq!(
!self.continue_execution,
self.action.is_some(),
"has_set_action out of sync"
);
debug_assert!(
self.continue_execution,
"action already set;\nold: {:#?}\nnew: {:#?}",
self.action, action,
);
self.continue_execution = false;
self.action = Some(action);
}
#[inline]
fn action(&mut self) -> &mut Option<InterpreterAction> {
&mut self.action
}
}
impl Jumps for ExtBytecode {
#[inline]
fn relative_jump(&mut self, offset: isize) {
self.instruction_pointer = unsafe { self.instruction_pointer.offset(offset) };
}
#[inline]
fn absolute_jump(&mut self, offset: usize) {
self.instruction_pointer = unsafe { self.base.bytes_ref().as_ptr().add(offset) };
}
#[inline]
fn is_valid_legacy_jump(&mut self, offset: usize) -> bool {
let jt = self.base.legacy_jump_table();
unsafe { jt.unwrap_unchecked() }.is_valid(offset)
}
#[inline]
fn opcode(&self) -> u8 {
unsafe { *self.instruction_pointer }
}
#[inline]
fn pc(&self) -> usize {
unsafe {
self.instruction_pointer
.offset_from_unsigned(self.base.bytes_ref().as_ptr())
}
}
}
impl Immediates for ExtBytecode {
#[inline]
fn read_u16(&self) -> u16 {
unsafe { read_u16(self.instruction_pointer) }
}
#[inline]
fn read_u8(&self) -> u8 {
unsafe { *self.instruction_pointer }
}
#[inline]
fn read_slice(&self, len: usize) -> &[u8] {
unsafe { core::slice::from_raw_parts(self.instruction_pointer, len) }
}
#[inline]
fn read_offset_u16(&self, offset: isize) -> u16 {
unsafe {
read_u16(
self.instruction_pointer
.offset(offset),
)
}
}
}
impl LegacyBytecode for ExtBytecode {
fn bytecode_len(&self) -> usize {
self.base.len()
}
fn bytecode_slice(&self) -> &[u8] {
self.base.original_byte_slice()
}
}
#[cfg(test)]
mod tests {
use super::*;
use primitives::Bytes;
#[test]
fn test_with_hash_constructor() {
let bytecode = Bytecode::new_raw(Bytes::from(&[0x60, 0x00][..]));
let hash = bytecode.hash_slow();
let ext_bytecode = ExtBytecode::new_with_hash(bytecode.clone(), hash);
assert_eq!(ext_bytecode.bytecode_hash, Some(hash));
}
}