mod binary;
mod comparison;
mod conversion;
mod unary;
use std::fmt;
use crate::metadata::typesystem::CilFlavor;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum BinaryOp {
Add,
AddOvf,
AddOvfUn,
Sub,
SubOvf,
SubOvfUn,
Mul,
MulOvf,
MulOvfUn,
Div,
DivUn,
Rem,
RemUn,
And,
Or,
Xor,
Shl,
Shr,
ShrUn,
}
impl BinaryOp {
#[must_use]
pub fn is_checked(&self) -> bool {
matches!(
self,
BinaryOp::AddOvf
| BinaryOp::AddOvfUn
| BinaryOp::SubOvf
| BinaryOp::SubOvfUn
| BinaryOp::MulOvf
| BinaryOp::MulOvfUn
)
}
#[must_use]
pub fn is_unsigned(&self) -> bool {
matches!(
self,
BinaryOp::AddOvfUn
| BinaryOp::SubOvfUn
| BinaryOp::MulOvfUn
| BinaryOp::DivUn
| BinaryOp::RemUn
| BinaryOp::ShrUn
)
}
#[must_use]
pub fn is_arithmetic(&self) -> bool {
matches!(
self,
BinaryOp::Add
| BinaryOp::AddOvf
| BinaryOp::AddOvfUn
| BinaryOp::Sub
| BinaryOp::SubOvf
| BinaryOp::SubOvfUn
| BinaryOp::Mul
| BinaryOp::MulOvf
| BinaryOp::MulOvfUn
| BinaryOp::Div
| BinaryOp::DivUn
| BinaryOp::Rem
| BinaryOp::RemUn
)
}
#[must_use]
pub fn is_bitwise(&self) -> bool {
matches!(
self,
BinaryOp::And
| BinaryOp::Or
| BinaryOp::Xor
| BinaryOp::Shl
| BinaryOp::Shr
| BinaryOp::ShrUn
)
}
}
impl fmt::Display for BinaryOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
BinaryOp::Add => write!(f, "add"),
BinaryOp::AddOvf => write!(f, "add.ovf"),
BinaryOp::AddOvfUn => write!(f, "add.ovf.un"),
BinaryOp::Sub => write!(f, "sub"),
BinaryOp::SubOvf => write!(f, "sub.ovf"),
BinaryOp::SubOvfUn => write!(f, "sub.ovf.un"),
BinaryOp::Mul => write!(f, "mul"),
BinaryOp::MulOvf => write!(f, "mul.ovf"),
BinaryOp::MulOvfUn => write!(f, "mul.ovf.un"),
BinaryOp::Div => write!(f, "div"),
BinaryOp::DivUn => write!(f, "div.un"),
BinaryOp::Rem => write!(f, "rem"),
BinaryOp::RemUn => write!(f, "rem.un"),
BinaryOp::And => write!(f, "and"),
BinaryOp::Or => write!(f, "or"),
BinaryOp::Xor => write!(f, "xor"),
BinaryOp::Shl => write!(f, "shl"),
BinaryOp::Shr => write!(f, "shr"),
BinaryOp::ShrUn => write!(f, "shr.un"),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum UnaryOp {
Neg,
Not,
}
impl fmt::Display for UnaryOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
UnaryOp::Neg => write!(f, "neg"),
UnaryOp::Not => write!(f, "not"),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum CompareOp {
Eq,
Ne,
Gt,
GtUn,
Ge,
GeUn,
Lt,
LtUn,
Le,
LeUn,
}
impl CompareOp {
#[must_use]
pub fn is_unsigned(&self) -> bool {
matches!(
self,
CompareOp::GtUn | CompareOp::GeUn | CompareOp::LtUn | CompareOp::LeUn
)
}
#[must_use]
pub fn negate(&self) -> Self {
match self {
CompareOp::Eq => CompareOp::Ne,
CompareOp::Ne => CompareOp::Eq,
CompareOp::Gt => CompareOp::Le,
CompareOp::GtUn => CompareOp::LeUn,
CompareOp::Ge => CompareOp::Lt,
CompareOp::GeUn => CompareOp::LtUn,
CompareOp::Lt => CompareOp::Ge,
CompareOp::LtUn => CompareOp::GeUn,
CompareOp::Le => CompareOp::Gt,
CompareOp::LeUn => CompareOp::GtUn,
}
}
#[must_use]
pub fn swap(&self) -> Self {
match self {
CompareOp::Eq => CompareOp::Eq,
CompareOp::Ne => CompareOp::Ne,
CompareOp::Gt => CompareOp::Lt,
CompareOp::GtUn => CompareOp::LtUn,
CompareOp::Ge => CompareOp::Le,
CompareOp::GeUn => CompareOp::LeUn,
CompareOp::Lt => CompareOp::Gt,
CompareOp::LtUn => CompareOp::GtUn,
CompareOp::Le => CompareOp::Ge,
CompareOp::LeUn => CompareOp::GeUn,
}
}
}
impl fmt::Display for CompareOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CompareOp::Eq => write!(f, "ceq"),
CompareOp::Ne => write!(f, "ne"),
CompareOp::Gt => write!(f, "cgt"),
CompareOp::GtUn => write!(f, "cgt.un"),
CompareOp::Ge => write!(f, "ge"),
CompareOp::GeUn => write!(f, "ge.un"),
CompareOp::Lt => write!(f, "clt"),
CompareOp::LtUn => write!(f, "clt.un"),
CompareOp::Le => write!(f, "le"),
CompareOp::LeUn => write!(f, "le.un"),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ConversionType {
I1,
I2,
I4,
I8,
U1,
U2,
U4,
U8,
R4,
R8,
I,
U,
RUn,
I1Ovf,
I2Ovf,
I4Ovf,
I8Ovf,
U1Ovf,
U2Ovf,
U4Ovf,
U8Ovf,
IOvf,
UOvf,
I1OvfUn,
I2OvfUn,
I4OvfUn,
I8OvfUn,
U1OvfUn,
U2OvfUn,
U4OvfUn,
U8OvfUn,
IOvfUn,
UOvfUn,
}
impl ConversionType {
#[must_use]
pub fn is_checked(&self) -> bool {
matches!(
self,
ConversionType::I1Ovf
| ConversionType::I2Ovf
| ConversionType::I4Ovf
| ConversionType::I8Ovf
| ConversionType::U1Ovf
| ConversionType::U2Ovf
| ConversionType::U4Ovf
| ConversionType::U8Ovf
| ConversionType::IOvf
| ConversionType::UOvf
| ConversionType::I1OvfUn
| ConversionType::I2OvfUn
| ConversionType::I4OvfUn
| ConversionType::I8OvfUn
| ConversionType::U1OvfUn
| ConversionType::U2OvfUn
| ConversionType::U4OvfUn
| ConversionType::U8OvfUn
| ConversionType::IOvfUn
| ConversionType::UOvfUn
)
}
#[must_use]
pub fn is_unsigned_source(&self) -> bool {
matches!(
self,
ConversionType::RUn
| ConversionType::I1OvfUn
| ConversionType::I2OvfUn
| ConversionType::I4OvfUn
| ConversionType::I8OvfUn
| ConversionType::U1OvfUn
| ConversionType::U2OvfUn
| ConversionType::U4OvfUn
| ConversionType::U8OvfUn
| ConversionType::IOvfUn
| ConversionType::UOvfUn
)
}
#[must_use]
pub fn target_cil_flavor(&self) -> CilFlavor {
match self {
ConversionType::I1
| ConversionType::I2
| ConversionType::I4
| ConversionType::U1
| ConversionType::U2
| ConversionType::U4
| ConversionType::I1Ovf
| ConversionType::I2Ovf
| ConversionType::I4Ovf
| ConversionType::U1Ovf
| ConversionType::U2Ovf
| ConversionType::U4Ovf
| ConversionType::I1OvfUn
| ConversionType::I2OvfUn
| ConversionType::I4OvfUn
| ConversionType::U1OvfUn
| ConversionType::U2OvfUn
| ConversionType::U4OvfUn => CilFlavor::I4,
ConversionType::I8
| ConversionType::U8
| ConversionType::I8Ovf
| ConversionType::U8Ovf
| ConversionType::I8OvfUn
| ConversionType::U8OvfUn => CilFlavor::I8,
ConversionType::R4 => CilFlavor::R4,
ConversionType::R8 | ConversionType::RUn => CilFlavor::R8,
ConversionType::I
| ConversionType::U
| ConversionType::IOvf
| ConversionType::UOvf
| ConversionType::IOvfUn
| ConversionType::UOvfUn => CilFlavor::I,
}
}
}
impl fmt::Display for ConversionType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ConversionType::I1 => write!(f, "conv.i1"),
ConversionType::I2 => write!(f, "conv.i2"),
ConversionType::I4 => write!(f, "conv.i4"),
ConversionType::I8 => write!(f, "conv.i8"),
ConversionType::U1 => write!(f, "conv.u1"),
ConversionType::U2 => write!(f, "conv.u2"),
ConversionType::U4 => write!(f, "conv.u4"),
ConversionType::U8 => write!(f, "conv.u8"),
ConversionType::R4 => write!(f, "conv.r4"),
ConversionType::R8 => write!(f, "conv.r8"),
ConversionType::I => write!(f, "conv.i"),
ConversionType::U => write!(f, "conv.u"),
ConversionType::RUn => write!(f, "conv.r.un"),
ConversionType::I1Ovf => write!(f, "conv.ovf.i1"),
ConversionType::I2Ovf => write!(f, "conv.ovf.i2"),
ConversionType::I4Ovf => write!(f, "conv.ovf.i4"),
ConversionType::I8Ovf => write!(f, "conv.ovf.i8"),
ConversionType::U1Ovf => write!(f, "conv.ovf.u1"),
ConversionType::U2Ovf => write!(f, "conv.ovf.u2"),
ConversionType::U4Ovf => write!(f, "conv.ovf.u4"),
ConversionType::U8Ovf => write!(f, "conv.ovf.u8"),
ConversionType::IOvf => write!(f, "conv.ovf.i"),
ConversionType::UOvf => write!(f, "conv.ovf.u"),
ConversionType::I1OvfUn => write!(f, "conv.ovf.i1.un"),
ConversionType::I2OvfUn => write!(f, "conv.ovf.i2.un"),
ConversionType::I4OvfUn => write!(f, "conv.ovf.i4.un"),
ConversionType::I8OvfUn => write!(f, "conv.ovf.i8.un"),
ConversionType::U1OvfUn => write!(f, "conv.ovf.u1.un"),
ConversionType::U2OvfUn => write!(f, "conv.ovf.u2.un"),
ConversionType::U4OvfUn => write!(f, "conv.ovf.u4.un"),
ConversionType::U8OvfUn => write!(f, "conv.ovf.u8.un"),
ConversionType::IOvfUn => write!(f, "conv.ovf.i.un"),
ConversionType::UOvfUn => write!(f, "conv.ovf.u.un"),
}
}
}
#[cfg(test)]
mod tests {
use crate::{
emulation::{
engine::EmulationError,
value::{BinaryOp, CompareOp, ConversionType, UnaryOp},
},
metadata::typesystem::CilFlavor,
};
#[test]
fn test_binary_op_is_checked() {
assert!(BinaryOp::AddOvf.is_checked());
assert!(BinaryOp::MulOvfUn.is_checked());
assert!(!BinaryOp::Add.is_checked());
assert!(!BinaryOp::Div.is_checked());
}
#[test]
fn test_binary_op_is_unsigned() {
assert!(BinaryOp::DivUn.is_unsigned());
assert!(BinaryOp::ShrUn.is_unsigned());
assert!(!BinaryOp::Div.is_unsigned());
assert!(!BinaryOp::Shr.is_unsigned());
}
#[test]
fn test_compare_op_negate() {
assert_eq!(CompareOp::Eq.negate(), CompareOp::Ne);
assert_eq!(CompareOp::Lt.negate(), CompareOp::Ge);
assert_eq!(CompareOp::Gt.negate(), CompareOp::Le);
}
#[test]
fn test_compare_op_swap() {
assert_eq!(CompareOp::Lt.swap(), CompareOp::Gt);
assert_eq!(CompareOp::Ge.swap(), CompareOp::Le);
assert_eq!(CompareOp::Eq.swap(), CompareOp::Eq);
}
#[test]
fn test_conversion_type_target_cil_flavor() {
assert_eq!(ConversionType::I1.target_cil_flavor(), CilFlavor::I4);
assert_eq!(ConversionType::I8.target_cil_flavor(), CilFlavor::I8);
assert_eq!(ConversionType::R4.target_cil_flavor(), CilFlavor::R4);
assert_eq!(ConversionType::R8.target_cil_flavor(), CilFlavor::R8);
assert_eq!(ConversionType::I.target_cil_flavor(), CilFlavor::I);
}
#[test]
fn test_binary_op_display() {
assert_eq!(format!("{}", BinaryOp::Add), "add");
assert_eq!(format!("{}", BinaryOp::AddOvf), "add.ovf");
assert_eq!(format!("{}", BinaryOp::DivUn), "div.un");
}
#[test]
fn test_unary_op_display() {
assert_eq!(format!("{}", UnaryOp::Neg), "neg");
assert_eq!(format!("{}", UnaryOp::Not), "not");
}
#[test]
fn test_compare_op_display() {
assert_eq!(format!("{}", CompareOp::Eq), "ceq");
assert_eq!(format!("{}", CompareOp::Lt), "clt");
assert_eq!(format!("{}", CompareOp::GtUn), "cgt.un");
}
#[test]
fn test_conversion_type_display() {
assert_eq!(format!("{}", ConversionType::I4), "conv.i4");
assert_eq!(format!("{}", ConversionType::I8Ovf), "conv.ovf.i8");
assert_eq!(format!("{}", ConversionType::U4OvfUn), "conv.ovf.u4.un");
}
#[test]
fn test_operation_error_display() {
let err = EmulationError::DivisionByZero;
assert_eq!(format!("{}", err), "division by zero");
let err = EmulationError::ArithmeticOverflow;
assert_eq!(format!("{}", err), "arithmetic overflow");
}
}