use super::JumpTable;
use crate::opcode;
use primitives::Bytes;
#[derive(Clone, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct LegacyAnalyzedBytecode {
bytecode: Bytes,
original_len: usize,
jump_table: JumpTable,
}
impl Default for LegacyAnalyzedBytecode {
#[inline]
fn default() -> Self {
Self {
bytecode: Bytes::from_static(&[0]),
original_len: 0,
jump_table: JumpTable::default(),
}
}
}
impl LegacyAnalyzedBytecode {
pub fn new(bytecode: Bytes, original_len: usize, jump_table: JumpTable) -> Self {
if original_len > bytecode.len() {
panic!("original_len is greater than bytecode length");
}
if original_len > jump_table.0.len() {
panic!(
"jump table length {} is less than original length {}",
jump_table.0.len(),
original_len
);
}
if bytecode.is_empty() {
panic!("bytecode cannot be empty");
}
if bytecode.last() != Some(&opcode::STOP) {
panic!("last bytecode byte should be STOP (0x00)");
}
Self {
bytecode,
original_len,
jump_table,
}
}
pub fn bytecode(&self) -> &Bytes {
&self.bytecode
}
pub fn original_len(&self) -> usize {
self.original_len
}
pub fn original_bytes(&self) -> Bytes {
self.bytecode.slice(..self.original_len)
}
pub fn original_byte_slice(&self) -> &[u8] {
&self.bytecode[..self.original_len]
}
pub fn jump_table(&self) -> &JumpTable {
&self.jump_table
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{opcode, LegacyRawBytecode};
use bitvec::{bitvec, order::Lsb0};
use std::sync::Arc;
#[test]
fn test_bytecode_new() {
let bytecode = Bytes::from_static(&[opcode::PUSH1, 0x01]);
let bytecode = LegacyRawBytecode(bytecode).into_analyzed();
let _ = LegacyAnalyzedBytecode::new(
bytecode.bytecode,
bytecode.original_len,
bytecode.jump_table,
);
}
#[test]
#[should_panic(expected = "original_len is greater than bytecode length")]
fn test_panic_on_large_original_len() {
let bytecode = Bytes::from_static(&[opcode::PUSH1, 0x01]);
let bytecode = LegacyRawBytecode(bytecode).into_analyzed();
let _ = LegacyAnalyzedBytecode::new(bytecode.bytecode, 100, bytecode.jump_table);
}
#[test]
#[should_panic(expected = "jump table length 1 is less than original length 2")]
fn test_panic_on_short_jump_table() {
let bytecode = Bytes::from_static(&[opcode::PUSH1, 0x01]);
let bytecode = LegacyRawBytecode(bytecode).into_analyzed();
let jump_table = JumpTable(Arc::new(bitvec![u8, Lsb0; 0; 1]));
let _ = LegacyAnalyzedBytecode::new(bytecode.bytecode, bytecode.original_len, jump_table);
}
#[test]
#[should_panic(expected = "last bytecode byte should be STOP (0x00)")]
fn test_panic_on_non_stop_bytecode() {
let bytecode = Bytes::from_static(&[opcode::PUSH1, 0x01]);
let jump_table = JumpTable(Arc::new(bitvec![u8, Lsb0; 0; 2]));
let _ = LegacyAnalyzedBytecode::new(bytecode, 2, jump_table);
}
#[test]
#[should_panic(expected = "bytecode cannot be empty")]
fn test_panic_on_empty_bytecode() {
let bytecode = Bytes::from_static(&[]);
let jump_table = JumpTable(Arc::new(bitvec![u8, Lsb0; 0; 0]));
let _ = LegacyAnalyzedBytecode::new(bytecode, 0, jump_table);
}
}