//! Pulley opcodes (without operands). macro_rules! define_opcode { ( $( $( #[$attr:meta] )* $snake_name:ident = $name:ident $( { $( $( #[$field_attr:meta] )* $field:ident : $field_ty:ty ),* } )? ; )* ) => { /// An opcode without its immediates and operands. #[repr(u8)] #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum Opcode { $( $( #[$attr] )* $name, )* /// The extended-op opcode. An `ExtendedOpcode` follows this opcode. ExtendedOp, } impl Opcode { /// The value of the maximum defined opcode. pub const MAX: u8 = define_opcode!( @max $( $name )* ) + 1; } }; ( @max $x:ident ) => { 0 }; ( @max $x:ident $( $xs:ident )* ) => { 1 + define_opcode!(@max $( $xs )* ) }; } for_each_op!(define_opcode); impl Opcode { /// Create a new `Opcode` from the given byte. /// /// Returns `None` if `byte` is not a valid opcode. pub fn new(byte: u8) -> Option { if byte <= Self::MAX { Some(unsafe { Self::unchecked_new(byte) }) } else { None } } /// Like `new` but does not check whether `byte` is a valid opcode. /// /// # Safety /// /// It is unsafe to pass a `byte` that is not a valid opcode. pub unsafe fn unchecked_new(byte: u8) -> Self { debug_assert!(byte <= Self::MAX); core::mem::transmute(byte) } } macro_rules! define_extended_opcode { ( $( $( #[$attr:meta] )* $snake_name:ident = $name:ident $( { $( $field:ident : $field_ty:ty ),* } )? ; )* ) => { /// An extended opcode. #[repr(u16)] #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum ExtendedOpcode { $( $( #[$attr] )* $name, )* } impl ExtendedOpcode { /// The value of the maximum defined extended opcode. pub const MAX: u16 = define_opcode!( @max $( $name )* ); } }; } for_each_extended_op!(define_extended_opcode); impl ExtendedOpcode { #[cfg_attr(not(feature = "interp"), allow(unused))] pub(crate) const ENCODED_SIZE_OF_TRAP: usize = 3; /// Create a new `ExtendedOpcode` from the given bytes. /// /// Returns `None` if `bytes` is not a valid extended opcode. pub fn new(bytes: u16) -> Option { if bytes <= Self::MAX { Some(unsafe { Self::unchecked_new(bytes) }) } else { None } } /// Like `new` but does not check whether `bytes` is a valid opcode. /// /// # Safety /// /// It is unsafe to pass `bytes` that is not a valid opcode. pub unsafe fn unchecked_new(byte: u16) -> Self { debug_assert!(byte <= Self::MAX); core::mem::transmute(byte) } } #[cfg(all(test, feature = "encode"))] mod tests { use super::*; use alloc::vec::Vec; #[test] fn encoded_size_of_trap() { let mut buf = Vec::new(); crate::encode::trap(&mut buf); assert_eq!(ExtendedOpcode::ENCODED_SIZE_OF_TRAP, buf.len()); } }