#![allow(
clippy::cast_possible_truncation,
clippy::cast_possible_wrap,
clippy::cast_sign_loss,
clippy::cast_precision_loss,
clippy::cast_lossless
)]
use std::fmt;
use crate::{
emulation::{
engine::EmulationError,
value::{EmValue, SymbolicValue, TaintSource},
},
metadata::typesystem::{CilFlavor, PointerSize},
Result,
};
#[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"),
}
}
}
impl EmValue {
pub fn binary_op(&self, other: &Self, op: BinaryOp, ptr_size: PointerSize) -> Result<Self> {
if let EmValue::Symbolic(ref sym) = self {
return Ok(EmValue::Symbolic(SymbolicValue::derived(
sym.cil_flavor.clone(),
TaintSource::Computation,
)));
}
if let EmValue::Symbolic(ref sym) = other {
return Ok(EmValue::Symbolic(SymbolicValue::derived(
sym.cil_flavor.clone(),
TaintSource::Computation,
)));
}
let result = match op {
BinaryOp::Add => self.add(other),
BinaryOp::AddOvf => self.add_ovf(other, false),
BinaryOp::AddOvfUn => self.add_ovf(other, true),
BinaryOp::Sub => self.sub(other),
BinaryOp::SubOvf => self.sub_ovf(other, false),
BinaryOp::SubOvfUn => self.sub_ovf(other, true),
BinaryOp::Mul => self.mul(other),
BinaryOp::MulOvf => self.mul_ovf(other, false),
BinaryOp::MulOvfUn => self.mul_ovf(other, true),
BinaryOp::Div => self.div(other, false),
BinaryOp::DivUn => self.div(other, true),
BinaryOp::Rem => self.rem(other, false),
BinaryOp::RemUn => self.rem(other, true),
BinaryOp::And => self.bitand(other),
BinaryOp::Or => self.bitor(other),
BinaryOp::Xor => self.bitxor(other),
BinaryOp::Shl => self.shl(other),
BinaryOp::Shr => self.shr(other, false),
BinaryOp::ShrUn => self.shr(other, true),
}?;
Ok(match result {
EmValue::NativeInt(v) => EmValue::NativeInt(ptr_size.mask_signed(v)),
EmValue::NativeUInt(v) => EmValue::NativeUInt(ptr_size.mask_unsigned(v)),
other => other,
})
}
pub fn unary_op(&self, op: UnaryOp, ptr_size: PointerSize) -> Result<Self> {
if let EmValue::Symbolic(ref sym) = self {
return Ok(EmValue::Symbolic(SymbolicValue::derived(
sym.cil_flavor.clone(),
TaintSource::Computation,
)));
}
let result = match op {
UnaryOp::Neg => self.neg(),
UnaryOp::Not => self.not(),
}?;
Ok(match result {
EmValue::NativeInt(v) => EmValue::NativeInt(ptr_size.mask_signed(v)),
EmValue::NativeUInt(v) => EmValue::NativeUInt(ptr_size.mask_unsigned(v)),
other => other,
})
}
pub fn compare(&self, other: &Self, op: CompareOp) -> Result<Self> {
if matches!(self, EmValue::Symbolic(_)) || matches!(other, EmValue::Symbolic(_)) {
return Ok(EmValue::Symbolic(SymbolicValue::derived(
CilFlavor::I4,
TaintSource::Computation,
)));
}
let result = match op {
CompareOp::Eq => self.cmp_eq(other)?,
CompareOp::Ne => !self.cmp_eq(other)?,
CompareOp::Lt => self.cmp_lt(other, false)?,
CompareOp::LtUn => self.cmp_lt(other, true)?,
CompareOp::Le => self.cmp_le(other, false)?,
CompareOp::LeUn => self.cmp_le(other, true)?,
CompareOp::Gt => self.cmp_gt(other, false)?,
CompareOp::GtUn => self.cmp_gt(other, true)?,
CompareOp::Ge => self.cmp_ge(other, false)?,
CompareOp::GeUn => self.cmp_ge(other, true)?,
};
Ok(EmValue::I32(i32::from(result)))
}
pub fn convert(&self, conv: ConversionType, ptr_size: PointerSize) -> Result<Self> {
if let EmValue::Symbolic(_) = self {
return Ok(EmValue::Symbolic(SymbolicValue::derived(
conv.target_cil_flavor(),
TaintSource::Computation,
)));
}
match conv {
ConversionType::I1 => self.conv_i1(),
ConversionType::I2 => self.conv_i2(),
ConversionType::I4 => self.conv_i4(),
ConversionType::I8 => self.conv_i8(),
ConversionType::U1 => self.conv_u1(),
ConversionType::U2 => self.conv_u2(),
ConversionType::U4 => self.conv_u4(),
ConversionType::U8 => self.conv_u8(),
ConversionType::R4 => self.conv_r4(),
ConversionType::R8 => self.conv_r8(),
ConversionType::I => self.conv_i(ptr_size),
ConversionType::U => self.conv_u(ptr_size),
ConversionType::RUn => self.conv_r_un(),
ConversionType::I1Ovf => self.conv_i1_ovf(false),
ConversionType::I2Ovf => self.conv_i2_ovf(false),
ConversionType::I4Ovf => self.conv_i4_ovf(false),
ConversionType::I8Ovf => self.conv_i8_ovf(false),
ConversionType::U1Ovf => self.conv_u1_ovf(false),
ConversionType::U2Ovf => self.conv_u2_ovf(false),
ConversionType::U4Ovf => self.conv_u4_ovf(false),
ConversionType::U8Ovf => self.conv_u8_ovf(false),
ConversionType::IOvf => self.conv_i_ovf(false),
ConversionType::UOvf => self.conv_u_ovf(false),
ConversionType::I1OvfUn => self.conv_i1_ovf(true),
ConversionType::I2OvfUn => self.conv_i2_ovf(true),
ConversionType::I4OvfUn => self.conv_i4_ovf(true),
ConversionType::I8OvfUn => self.conv_i8_ovf(true),
ConversionType::U1OvfUn => self.conv_u1_ovf(true),
ConversionType::U2OvfUn => self.conv_u2_ovf(true),
ConversionType::U4OvfUn => self.conv_u4_ovf(true),
ConversionType::U8OvfUn => self.conv_u8_ovf(true),
ConversionType::IOvfUn => self.conv_i_ovf(true),
ConversionType::UOvfUn => self.conv_u_ovf(true),
}
}
fn add(&self, other: &Self) -> Result<Self> {
match (self, other) {
(EmValue::I32(a), EmValue::I32(b)) => Ok(EmValue::I32(a.wrapping_add(*b))),
(EmValue::I64(a), EmValue::I64(b)) => Ok(EmValue::I64(a.wrapping_add(*b))),
(EmValue::NativeInt(a), EmValue::NativeInt(b)) => {
Ok(EmValue::NativeInt(a.wrapping_add(*b)))
}
(EmValue::NativeUInt(a), EmValue::NativeUInt(b)) => {
Ok(EmValue::NativeUInt(a.wrapping_add(*b)))
}
(EmValue::F32(a), EmValue::F32(b)) => Ok(EmValue::F32(a + b)),
(EmValue::F64(a), EmValue::F64(b)) => Ok(EmValue::F64(a + b)),
(EmValue::NativeInt(a), EmValue::I32(b)) => {
Ok(EmValue::NativeInt(a.wrapping_add(i64::from(*b))))
}
(EmValue::I32(a), EmValue::NativeInt(b)) => {
Ok(EmValue::NativeInt(i64::from(*a).wrapping_add(*b)))
}
(EmValue::NativeUInt(a), EmValue::NativeInt(b)) => {
Ok(EmValue::NativeUInt(a.wrapping_add(*b as u64)))
}
(EmValue::NativeInt(a), EmValue::NativeUInt(b)) => {
Ok(EmValue::NativeUInt((*a as u64).wrapping_add(*b)))
}
(EmValue::NativeUInt(a), EmValue::I32(b)) => {
Ok(EmValue::NativeUInt(a.wrapping_add(*b as u64)))
}
(EmValue::I32(a), EmValue::NativeUInt(b)) => {
Ok(EmValue::NativeUInt((*a as u64).wrapping_add(*b)))
}
(EmValue::UnmanagedPtr(a), EmValue::I32(b)) => {
Ok(EmValue::UnmanagedPtr(a.wrapping_add(*b as u64)))
}
(EmValue::I32(a), EmValue::UnmanagedPtr(b)) => {
Ok(EmValue::UnmanagedPtr((*a as u64).wrapping_add(*b)))
}
(EmValue::UnmanagedPtr(a), EmValue::NativeInt(b)) => {
Ok(EmValue::UnmanagedPtr(a.wrapping_add(*b as u64)))
}
(EmValue::NativeInt(a), EmValue::UnmanagedPtr(b)) => {
Ok(EmValue::UnmanagedPtr((*a as u64).wrapping_add(*b)))
}
(EmValue::UnmanagedPtr(a), EmValue::NativeUInt(b))
| (EmValue::NativeUInt(a), EmValue::UnmanagedPtr(b)) => {
Ok(EmValue::UnmanagedPtr(a.wrapping_add(*b)))
}
(a, b) => Err(EmulationError::InvalidOperationTypes {
operation: "add".to_string(),
operand_types: format!("{}, {}", a.cil_flavor(), b.cil_flavor()),
}
.into()),
}
}
fn add_ovf(&self, other: &Self, unsigned: bool) -> Result<Self> {
match (self, other) {
(EmValue::I32(a), EmValue::I32(b)) => {
if unsigned {
let (result, overflow) = (*a as u32).overflowing_add(*b as u32);
if overflow {
Err(EmulationError::ArithmeticOverflow.into())
} else {
Ok(EmValue::I32(result as i32))
}
} else {
a.checked_add(*b)
.map(EmValue::I32)
.ok_or_else(|| EmulationError::ArithmeticOverflow.into())
}
}
(EmValue::I64(a), EmValue::I64(b)) => {
if unsigned {
let (result, overflow) = (*a as u64).overflowing_add(*b as u64);
if overflow {
Err(EmulationError::ArithmeticOverflow.into())
} else {
Ok(EmValue::I64(result as i64))
}
} else {
a.checked_add(*b)
.map(EmValue::I64)
.ok_or_else(|| EmulationError::ArithmeticOverflow.into())
}
}
_ => Err(EmulationError::InvalidOperationTypes {
operation: if unsigned { "add.ovf.un" } else { "add.ovf" }.to_string(),
operand_types: format!("{}, {}", self.cil_flavor(), other.cil_flavor()),
}
.into()),
}
}
fn sub(&self, other: &Self) -> Result<Self> {
match (self, other) {
(EmValue::I32(a), EmValue::I32(b)) => Ok(EmValue::I32(a.wrapping_sub(*b))),
(EmValue::I64(a), EmValue::I64(b)) => Ok(EmValue::I64(a.wrapping_sub(*b))),
(EmValue::NativeInt(a), EmValue::NativeInt(b)) => {
Ok(EmValue::NativeInt(a.wrapping_sub(*b)))
}
(EmValue::NativeUInt(a), EmValue::NativeUInt(b)) => {
Ok(EmValue::NativeUInt(a.wrapping_sub(*b)))
}
(EmValue::F32(a), EmValue::F32(b)) => Ok(EmValue::F32(a - b)),
(EmValue::F64(a), EmValue::F64(b)) => Ok(EmValue::F64(a - b)),
(EmValue::NativeInt(a), EmValue::I32(b)) => {
Ok(EmValue::NativeInt(a.wrapping_sub(i64::from(*b))))
}
(EmValue::I32(a), EmValue::NativeInt(b)) => {
Ok(EmValue::NativeInt(i64::from(*a).wrapping_sub(*b)))
}
(EmValue::NativeUInt(a), EmValue::NativeInt(b)) => {
Ok(EmValue::NativeUInt(a.wrapping_sub(*b as u64)))
}
(EmValue::NativeInt(a), EmValue::NativeUInt(b)) => {
Ok(EmValue::NativeUInt((*a as u64).wrapping_sub(*b)))
}
(EmValue::NativeUInt(a), EmValue::I32(b)) => {
Ok(EmValue::NativeUInt(a.wrapping_sub(*b as u64)))
}
(EmValue::I32(a), EmValue::NativeUInt(b)) => {
Ok(EmValue::NativeUInt((*a as u64).wrapping_sub(*b)))
}
(EmValue::UnmanagedPtr(a), EmValue::I32(b)) => {
Ok(EmValue::UnmanagedPtr(a.wrapping_sub(*b as u64)))
}
(EmValue::UnmanagedPtr(a), EmValue::NativeInt(b)) => {
Ok(EmValue::UnmanagedPtr(a.wrapping_sub(*b as u64)))
}
(EmValue::UnmanagedPtr(a), EmValue::NativeUInt(b)) => {
Ok(EmValue::UnmanagedPtr(a.wrapping_sub(*b)))
}
(EmValue::UnmanagedPtr(a), EmValue::UnmanagedPtr(b)) => {
Ok(EmValue::NativeInt(a.wrapping_sub(*b) as i64))
}
(a, b) => Err(EmulationError::InvalidOperationTypes {
operation: "sub".to_string(),
operand_types: format!("{}, {}", a.cil_flavor(), b.cil_flavor()),
}
.into()),
}
}
fn sub_ovf(&self, other: &Self, unsigned: bool) -> Result<Self> {
match (self, other) {
(EmValue::I32(a), EmValue::I32(b)) => {
if unsigned {
let (result, overflow) = (*a as u32).overflowing_sub(*b as u32);
if overflow {
Err(EmulationError::ArithmeticOverflow.into())
} else {
Ok(EmValue::I32(result as i32))
}
} else {
a.checked_sub(*b)
.map(EmValue::I32)
.ok_or_else(|| EmulationError::ArithmeticOverflow.into())
}
}
(EmValue::I64(a), EmValue::I64(b)) => {
if unsigned {
let (result, overflow) = (*a as u64).overflowing_sub(*b as u64);
if overflow {
Err(EmulationError::ArithmeticOverflow.into())
} else {
Ok(EmValue::I64(result as i64))
}
} else {
a.checked_sub(*b)
.map(EmValue::I64)
.ok_or_else(|| EmulationError::ArithmeticOverflow.into())
}
}
_ => Err(EmulationError::InvalidOperationTypes {
operation: if unsigned { "sub.ovf.un" } else { "sub.ovf" }.to_string(),
operand_types: format!("{}, {}", self.cil_flavor(), other.cil_flavor()),
}
.into()),
}
}
fn mul(&self, other: &Self) -> Result<Self> {
match (self, other) {
(EmValue::I32(a), EmValue::I32(b)) => Ok(EmValue::I32(a.wrapping_mul(*b))),
(EmValue::I64(a), EmValue::I64(b)) => Ok(EmValue::I64(a.wrapping_mul(*b))),
(EmValue::NativeInt(a), EmValue::NativeInt(b)) => {
Ok(EmValue::NativeInt(a.wrapping_mul(*b)))
}
(EmValue::NativeUInt(a), EmValue::NativeUInt(b)) => {
Ok(EmValue::NativeUInt(a.wrapping_mul(*b)))
}
(EmValue::F32(a), EmValue::F32(b)) => Ok(EmValue::F32(a * b)),
(EmValue::F64(a), EmValue::F64(b)) => Ok(EmValue::F64(a * b)),
(EmValue::NativeInt(a), EmValue::I32(b)) => {
Ok(EmValue::NativeInt(a.wrapping_mul(i64::from(*b))))
}
(EmValue::I32(a), EmValue::NativeInt(b)) => {
Ok(EmValue::NativeInt(i64::from(*a).wrapping_mul(*b)))
}
(EmValue::NativeUInt(a), EmValue::NativeInt(b)) => {
Ok(EmValue::NativeUInt(a.wrapping_mul(*b as u64)))
}
(EmValue::NativeInt(a), EmValue::NativeUInt(b)) => {
Ok(EmValue::NativeUInt((*a as u64).wrapping_mul(*b)))
}
(EmValue::NativeUInt(a), EmValue::I32(b)) => {
Ok(EmValue::NativeUInt(a.wrapping_mul(*b as u64)))
}
(EmValue::I32(a), EmValue::NativeUInt(b)) => {
Ok(EmValue::NativeUInt((*a as u64).wrapping_mul(*b)))
}
(a, b) => Err(EmulationError::InvalidOperationTypes {
operation: "mul".to_string(),
operand_types: format!("{}, {}", a.cil_flavor(), b.cil_flavor()),
}
.into()),
}
}
fn mul_ovf(&self, other: &Self, unsigned: bool) -> Result<Self> {
match (self, other) {
(EmValue::I32(a), EmValue::I32(b)) => {
if unsigned {
let ua = *a as u32;
let ub = *b as u32;
ua.checked_mul(ub)
.map(|r| EmValue::I32(r as i32))
.ok_or_else(|| EmulationError::ArithmeticOverflow.into())
} else {
a.checked_mul(*b)
.map(EmValue::I32)
.ok_or_else(|| EmulationError::ArithmeticOverflow.into())
}
}
(EmValue::I64(a), EmValue::I64(b)) => {
if unsigned {
let ua = *a as u64;
let ub = *b as u64;
ua.checked_mul(ub)
.map(|r| EmValue::I64(r as i64))
.ok_or_else(|| EmulationError::ArithmeticOverflow.into())
} else {
a.checked_mul(*b)
.map(EmValue::I64)
.ok_or_else(|| EmulationError::ArithmeticOverflow.into())
}
}
_ => Err(EmulationError::InvalidOperationTypes {
operation: if unsigned { "mul.ovf.un" } else { "mul.ovf" }.to_string(),
operand_types: format!("{}, {}", self.cil_flavor(), other.cil_flavor()),
}
.into()),
}
}
fn div(&self, other: &Self, unsigned: bool) -> Result<Self> {
let is_zero = match other {
EmValue::I32(v) => *v == 0,
EmValue::I64(v) | EmValue::NativeInt(v) => *v == 0,
EmValue::NativeUInt(v) => *v == 0,
EmValue::F32(v) => *v == 0.0,
EmValue::F64(v) => *v == 0.0,
_ => false,
};
if is_zero && !matches!(other, EmValue::F32(_) | EmValue::F64(_)) {
return Err(EmulationError::DivisionByZero.into());
}
match (self, other) {
(EmValue::I32(a), EmValue::I32(b)) => {
if unsigned {
Ok(EmValue::I32(((*a as u32) / (*b as u32)) as i32))
} else {
if *a == i32::MIN && *b == -1 {
Ok(EmValue::I32(i32::MIN)) } else {
Ok(EmValue::I32(a / b))
}
}
}
(EmValue::I64(a), EmValue::I64(b)) => {
if unsigned {
Ok(EmValue::I64(((*a as u64) / (*b as u64)) as i64))
} else if *a == i64::MIN && *b == -1 {
Ok(EmValue::I64(i64::MIN))
} else {
Ok(EmValue::I64(a / b))
}
}
(EmValue::NativeInt(a), EmValue::NativeInt(b)) => {
if unsigned {
Ok(EmValue::NativeInt(((*a as u64) / (*b as u64)) as i64))
} else if *a == i64::MIN && *b == -1 {
Ok(EmValue::NativeInt(i64::MIN))
} else {
Ok(EmValue::NativeInt(a / b))
}
}
(EmValue::NativeUInt(a), EmValue::NativeUInt(b)) => Ok(EmValue::NativeUInt(a / b)),
(EmValue::F32(a), EmValue::F32(b)) => Ok(EmValue::F32(a / b)),
(EmValue::F64(a), EmValue::F64(b)) => Ok(EmValue::F64(a / b)),
(a, b) => Err(EmulationError::InvalidOperationTypes {
operation: if unsigned { "div.un" } else { "div" }.to_string(),
operand_types: format!("{}, {}", a.cil_flavor(), b.cil_flavor()),
}
.into()),
}
}
fn rem(&self, other: &Self, unsigned: bool) -> Result<Self> {
let is_zero = match other {
EmValue::I32(v) => *v == 0,
EmValue::I64(v) | EmValue::NativeInt(v) => *v == 0,
EmValue::NativeUInt(v) => *v == 0,
_ => false,
};
if is_zero {
return Err(EmulationError::DivisionByZero.into());
}
match (self, other) {
(EmValue::I32(a), EmValue::I32(b)) => {
if unsigned {
Ok(EmValue::I32(((*a as u32) % (*b as u32)) as i32))
} else {
if *a == i32::MIN && *b == -1 {
Ok(EmValue::I32(0))
} else {
Ok(EmValue::I32(a % b))
}
}
}
(EmValue::I64(a), EmValue::I64(b)) => {
if unsigned {
Ok(EmValue::I64(((*a as u64) % (*b as u64)) as i64))
} else if *a == i64::MIN && *b == -1 {
Ok(EmValue::I64(0))
} else {
Ok(EmValue::I64(a % b))
}
}
(EmValue::NativeInt(a), EmValue::NativeInt(b)) => {
if unsigned {
Ok(EmValue::NativeInt(((*a as u64) % (*b as u64)) as i64))
} else if *a == i64::MIN && *b == -1 {
Ok(EmValue::NativeInt(0))
} else {
Ok(EmValue::NativeInt(a % b))
}
}
(EmValue::NativeUInt(a), EmValue::NativeUInt(b)) => Ok(EmValue::NativeUInt(a % b)),
(EmValue::F32(a), EmValue::F32(b)) => Ok(EmValue::F32(a % b)),
(EmValue::F64(a), EmValue::F64(b)) => Ok(EmValue::F64(a % b)),
(a, b) => Err(EmulationError::InvalidOperationTypes {
operation: if unsigned { "rem.un" } else { "rem" }.to_string(),
operand_types: format!("{}, {}", a.cil_flavor(), b.cil_flavor()),
}
.into()),
}
}
fn bitand(&self, other: &Self) -> Result<Self> {
match (self, other) {
(EmValue::I32(a), EmValue::I32(b)) => Ok(EmValue::I32(a & b)),
(EmValue::I64(a), EmValue::I64(b)) => Ok(EmValue::I64(a & b)),
(EmValue::NativeInt(a), EmValue::NativeInt(b)) => Ok(EmValue::NativeInt(a & b)),
(EmValue::NativeUInt(a), EmValue::NativeUInt(b)) => Ok(EmValue::NativeUInt(a & b)),
(a, b) => Err(EmulationError::InvalidOperationTypes {
operation: "and".to_string(),
operand_types: format!("{}, {}", a.cil_flavor(), b.cil_flavor()),
}
.into()),
}
}
fn bitor(&self, other: &Self) -> Result<Self> {
match (self, other) {
(EmValue::I32(a), EmValue::I32(b)) => Ok(EmValue::I32(a | b)),
(EmValue::I64(a), EmValue::I64(b)) => Ok(EmValue::I64(a | b)),
(EmValue::NativeInt(a), EmValue::NativeInt(b)) => Ok(EmValue::NativeInt(a | b)),
(EmValue::NativeUInt(a), EmValue::NativeUInt(b)) => Ok(EmValue::NativeUInt(a | b)),
(a, b) => Err(EmulationError::InvalidOperationTypes {
operation: "or".to_string(),
operand_types: format!("{}, {}", a.cil_flavor(), b.cil_flavor()),
}
.into()),
}
}
fn bitxor(&self, other: &Self) -> Result<Self> {
match (self, other) {
(EmValue::I32(a), EmValue::I32(b)) => Ok(EmValue::I32(a ^ b)),
(EmValue::I64(a), EmValue::I64(b)) => Ok(EmValue::I64(a ^ b)),
(EmValue::NativeInt(a), EmValue::NativeInt(b)) => Ok(EmValue::NativeInt(a ^ b)),
(EmValue::NativeUInt(a), EmValue::NativeUInt(b)) => Ok(EmValue::NativeUInt(a ^ b)),
(a, b) => Err(EmulationError::InvalidOperationTypes {
operation: "xor".to_string(),
operand_types: format!("{}, {}", a.cil_flavor(), b.cil_flavor()),
}
.into()),
}
}
fn shl(&self, other: &Self) -> Result<Self> {
let shift = match *other {
EmValue::I32(v) => v as u32,
EmValue::I64(v) | EmValue::NativeInt(v) => v as u32,
EmValue::NativeUInt(v) => v as u32,
_ => {
return Err(EmulationError::InvalidOperationTypes {
operation: "shl".to_string(),
operand_types: format!("_, {}", other.cil_flavor()),
}
.into());
}
};
match *self {
EmValue::I32(a) => Ok(EmValue::I32(a.wrapping_shl(shift & 31))),
EmValue::I64(a) => Ok(EmValue::I64(a.wrapping_shl(shift & 63))),
EmValue::NativeInt(a) => Ok(EmValue::NativeInt(a.wrapping_shl(shift & 63))),
EmValue::NativeUInt(a) => Ok(EmValue::NativeUInt(a.wrapping_shl(shift & 63))),
_ => Err(EmulationError::InvalidOperationTypes {
operation: "shl".to_string(),
operand_types: format!("{}, _", self.cil_flavor()),
}
.into()),
}
}
fn shr(&self, other: &Self, unsigned: bool) -> Result<Self> {
let shift = match *other {
EmValue::I32(v) => v as u32,
EmValue::I64(v) | EmValue::NativeInt(v) => v as u32,
EmValue::NativeUInt(v) => v as u32,
_ => {
return Err(EmulationError::InvalidOperationTypes {
operation: if unsigned { "shr.un" } else { "shr" }.to_string(),
operand_types: format!("_, {}", other.cil_flavor()),
}
.into());
}
};
match *self {
EmValue::I32(a) => {
if unsigned {
Ok(EmValue::I32((a as u32).wrapping_shr(shift & 31) as i32))
} else {
Ok(EmValue::I32(a.wrapping_shr(shift & 31)))
}
}
EmValue::I64(a) => {
if unsigned {
Ok(EmValue::I64((a as u64).wrapping_shr(shift & 63) as i64))
} else {
Ok(EmValue::I64(a.wrapping_shr(shift & 63)))
}
}
EmValue::NativeInt(a) => {
if unsigned {
Ok(EmValue::NativeInt(
(a as u64).wrapping_shr(shift & 63) as i64
))
} else {
Ok(EmValue::NativeInt(a.wrapping_shr(shift & 63)))
}
}
EmValue::NativeUInt(a) => Ok(EmValue::NativeUInt(a.wrapping_shr(shift & 63))),
_ => Err(EmulationError::InvalidOperationTypes {
operation: if unsigned { "shr.un" } else { "shr" }.to_string(),
operand_types: format!("{}, _", self.cil_flavor()),
}
.into()),
}
}
fn neg(&self) -> Result<Self> {
match *self {
EmValue::I32(a) => Ok(EmValue::I32(a.wrapping_neg())),
EmValue::I64(a) => Ok(EmValue::I64(a.wrapping_neg())),
EmValue::NativeInt(a) => Ok(EmValue::NativeInt(a.wrapping_neg())),
EmValue::F32(a) => Ok(EmValue::F32(-a)),
EmValue::F64(a) => Ok(EmValue::F64(-a)),
_ => Err(EmulationError::InvalidOperationTypes {
operation: "neg".to_string(),
operand_types: self.cil_flavor().to_string(),
}
.into()),
}
}
fn not(&self) -> Result<Self> {
match *self {
EmValue::I32(a) => Ok(EmValue::I32(!a)),
EmValue::I64(a) => Ok(EmValue::I64(!a)),
EmValue::NativeInt(a) => Ok(EmValue::NativeInt(!a)),
EmValue::NativeUInt(a) => Ok(EmValue::NativeUInt(!a)),
_ => Err(EmulationError::InvalidOperationTypes {
operation: "not".to_string(),
operand_types: self.cil_flavor().to_string(),
}
.into()),
}
}
fn cmp_eq(&self, other: &Self) -> Result<bool> {
match (self, other) {
(EmValue::I32(a), EmValue::I32(b)) => Ok(a == b),
(EmValue::I64(a) | EmValue::NativeInt(a), EmValue::I64(b))
| (EmValue::NativeInt(a), EmValue::NativeInt(b))
| (EmValue::I64(b), EmValue::NativeInt(a)) => Ok(a == b),
(EmValue::NativeUInt(a), EmValue::NativeUInt(b))
| (EmValue::UnmanagedPtr(a), EmValue::UnmanagedPtr(b)) => Ok(a == b),
(EmValue::F32(a), EmValue::F32(b)) => Ok(a == b),
(EmValue::F64(a), EmValue::F64(b)) => Ok(a == b),
(EmValue::ObjectRef(a), EmValue::ObjectRef(b)) => Ok(a == b),
(EmValue::Null, EmValue::Null) => Ok(true),
(EmValue::ObjectRef(_), EmValue::Null) | (EmValue::Null, EmValue::ObjectRef(_)) => {
Ok(false)
}
(EmValue::Bool(a), EmValue::Bool(b)) => Ok(a == b),
(EmValue::Char(a), EmValue::Char(b)) => Ok(a == b),
(EmValue::I32(a), EmValue::Bool(b)) | (EmValue::Bool(b), EmValue::I32(a)) => {
Ok(*a == i32::from(*b))
}
(EmValue::I32(a), EmValue::Char(b)) | (EmValue::Char(b), EmValue::I32(a)) => {
Ok(*a == (*b as u32) as i32)
}
(EmValue::Bool(a), EmValue::Char(b)) | (EmValue::Char(b), EmValue::Bool(a)) => {
Ok(i32::from(*a) == (*b as u32) as i32)
}
(EmValue::NativeInt(a), EmValue::UnmanagedPtr(b))
| (EmValue::UnmanagedPtr(b), EmValue::NativeInt(a)) => Ok(*a as u64 == *b),
(a, b) => Err(EmulationError::InvalidOperationTypes {
operation: "ceq".to_string(),
operand_types: format!("{}, {}", a.cil_flavor(), b.cil_flavor()),
}
.into()),
}
}
fn cmp_lt(&self, other: &Self, unsigned: bool) -> Result<bool> {
match (self, other) {
(EmValue::I32(a), EmValue::I32(b)) => {
if unsigned {
Ok((*a as u32) < (*b as u32))
} else {
Ok(a < b)
}
}
(EmValue::I64(a), EmValue::I64(b)) | (EmValue::NativeInt(a), EmValue::NativeInt(b)) => {
if unsigned {
Ok((*a as u64) < (*b as u64))
} else {
Ok(a < b)
}
}
(EmValue::NativeUInt(a), EmValue::NativeUInt(b)) => Ok(a < b),
(EmValue::F32(a), EmValue::F32(b)) => {
if unsigned {
Ok(a < b || a.is_nan() || b.is_nan())
} else {
Ok(a < b)
}
}
(EmValue::F64(a), EmValue::F64(b)) => {
if unsigned {
Ok(a < b || a.is_nan() || b.is_nan())
} else {
Ok(a < b)
}
}
(a, b) => Err(EmulationError::InvalidOperationTypes {
operation: if unsigned { "clt.un" } else { "clt" }.to_string(),
operand_types: format!("{}, {}", a.cil_flavor(), b.cil_flavor()),
}
.into()),
}
}
fn cmp_le(&self, other: &Self, unsigned: bool) -> Result<bool> {
self.cmp_gt(other, unsigned).map(|r| !r)
}
fn cmp_gt(&self, other: &Self, unsigned: bool) -> Result<bool> {
match (self, other) {
(EmValue::I32(a), EmValue::I32(b)) => {
if unsigned {
Ok((*a as u32) > (*b as u32))
} else {
Ok(a > b)
}
}
(EmValue::I64(a), EmValue::I64(b)) | (EmValue::NativeInt(a), EmValue::NativeInt(b)) => {
if unsigned {
Ok((*a as u64) > (*b as u64))
} else {
Ok(a > b)
}
}
(EmValue::NativeUInt(a), EmValue::NativeUInt(b)) => Ok(a > b),
(EmValue::F32(a), EmValue::F32(b)) => {
if unsigned {
Ok(a > b || a.is_nan() || b.is_nan())
} else {
Ok(a > b)
}
}
(EmValue::F64(a), EmValue::F64(b)) => {
if unsigned {
Ok(a > b || a.is_nan() || b.is_nan())
} else {
Ok(a > b)
}
}
(a, b) => Err(EmulationError::InvalidOperationTypes {
operation: if unsigned { "cgt.un" } else { "cgt" }.to_string(),
operand_types: format!("{}, {}", a.cil_flavor(), b.cil_flavor()),
}
.into()),
}
}
fn cmp_ge(&self, other: &Self, unsigned: bool) -> Result<bool> {
self.cmp_lt(other, unsigned).map(|r| !r)
}
fn conv_i1(&self) -> Result<Self> {
let value = self.to_i64()?;
Ok(EmValue::I32(value as i8 as i32))
}
fn conv_i2(&self) -> Result<Self> {
let value = self.to_i64()?;
Ok(EmValue::I32(value as i16 as i32))
}
fn conv_i4(&self) -> Result<Self> {
let value = self.to_i64()?;
Ok(EmValue::I32(value as i32))
}
fn conv_i8(&self) -> Result<Self> {
let value = self.to_i64()?;
Ok(EmValue::I64(value))
}
fn conv_u1(&self) -> Result<Self> {
let value = self.to_i64()?;
Ok(EmValue::I32((value as u8) as i32))
}
fn conv_u2(&self) -> Result<Self> {
let value = self.to_i64()?;
Ok(EmValue::I32((value as u16) as i32))
}
fn conv_u4(&self) -> Result<Self> {
let value = self.to_i64()?;
Ok(EmValue::I32(value as u32 as i32))
}
fn conv_u8(&self) -> Result<Self> {
let value = self.to_u64()?;
Ok(EmValue::I64(value as i64))
}
fn conv_r4(&self) -> Result<Self> {
match *self {
EmValue::I32(v) => Ok(EmValue::F32(v as f32)),
EmValue::I64(v) | EmValue::NativeInt(v) => Ok(EmValue::F32(v as f32)),
EmValue::NativeUInt(v) => Ok(EmValue::F32(v as f32)),
EmValue::F32(v) => Ok(EmValue::F32(v)),
EmValue::F64(v) => Ok(EmValue::F32(v as f32)),
_ => Err(EmulationError::InvalidOperationTypes {
operation: "conv.r4".to_string(),
operand_types: self.cil_flavor().to_string(),
}
.into()),
}
}
fn conv_r8(&self) -> Result<Self> {
match *self {
EmValue::I32(v) => Ok(EmValue::F64(f64::from(v))),
EmValue::I64(v) | EmValue::NativeInt(v) => Ok(EmValue::F64(v as f64)),
EmValue::NativeUInt(v) => Ok(EmValue::F64(v as f64)),
EmValue::F32(v) => Ok(EmValue::F64(f64::from(v))),
EmValue::F64(v) => Ok(EmValue::F64(v)),
_ => Err(EmulationError::InvalidOperationTypes {
operation: "conv.r8".to_string(),
operand_types: self.cil_flavor().to_string(),
}
.into()),
}
}
fn conv_i(&self, ptr_size: PointerSize) -> Result<Self> {
let value = ptr_size.mask_signed(self.to_i64()?);
Ok(EmValue::NativeInt(value))
}
fn conv_u(&self, ptr_size: PointerSize) -> Result<Self> {
let value = ptr_size.mask_unsigned(self.to_u64()?);
Ok(EmValue::NativeUInt(value))
}
fn conv_r_un(&self) -> Result<Self> {
let value = self.to_u64()?;
Ok(EmValue::F64(value as f64))
}
fn conv_i1_ovf(&self, unsigned_source: bool) -> Result<Self> {
let value = if unsigned_source {
self.to_u64()? as i64
} else {
self.to_i64()?
};
if value < i8::MIN as i64 || value > i8::MAX as i64 {
Err(EmulationError::ArithmeticOverflow.into())
} else {
Ok(EmValue::I32(value as i32))
}
}
fn conv_i2_ovf(&self, unsigned_source: bool) -> Result<Self> {
let value = if unsigned_source {
self.to_u64()? as i64
} else {
self.to_i64()?
};
if value < i16::MIN as i64 || value > i16::MAX as i64 {
Err(EmulationError::ArithmeticOverflow.into())
} else {
Ok(EmValue::I32(value as i32))
}
}
fn conv_i4_ovf(&self, unsigned_source: bool) -> Result<Self> {
let value = if unsigned_source {
self.to_u64()? as i64
} else {
self.to_i64()?
};
if value < i32::MIN as i64 || value > i32::MAX as i64 {
Err(EmulationError::ArithmeticOverflow.into())
} else {
Ok(EmValue::I32(value as i32))
}
}
fn conv_i8_ovf(&self, unsigned_source: bool) -> Result<Self> {
if unsigned_source {
let value = self.to_u64()?;
if value > i64::MAX as u64 {
Err(EmulationError::ArithmeticOverflow.into())
} else {
Ok(EmValue::I64(value as i64))
}
} else {
let value = self.to_i64()?;
Ok(EmValue::I64(value))
}
}
fn conv_u1_ovf(&self, unsigned_source: bool) -> Result<Self> {
let value = if unsigned_source {
self.to_u64()?
} else {
let signed = self.to_i64()?;
if signed < 0 {
return Err(EmulationError::ArithmeticOverflow.into());
}
signed as u64
};
if value > u8::MAX as u64 {
Err(EmulationError::ArithmeticOverflow.into())
} else {
Ok(EmValue::I32(value as i32))
}
}
fn conv_u2_ovf(&self, unsigned_source: bool) -> Result<Self> {
let value = if unsigned_source {
self.to_u64()?
} else {
let signed = self.to_i64()?;
if signed < 0 {
return Err(EmulationError::ArithmeticOverflow.into());
}
signed as u64
};
if value > u16::MAX as u64 {
Err(EmulationError::ArithmeticOverflow.into())
} else {
Ok(EmValue::I32(value as i32))
}
}
fn conv_u4_ovf(&self, unsigned_source: bool) -> Result<Self> {
let value = if unsigned_source {
self.to_u64()?
} else {
let signed = self.to_i64()?;
if signed < 0 {
return Err(EmulationError::ArithmeticOverflow.into());
}
signed as u64
};
if value > u32::MAX as u64 {
Err(EmulationError::ArithmeticOverflow.into())
} else {
Ok(EmValue::I32(value as u32 as i32))
}
}
fn conv_u8_ovf(&self, unsigned_source: bool) -> Result<Self> {
if unsigned_source {
let value = self.to_u64()?;
Ok(EmValue::I64(value as i64))
} else {
let value = self.to_i64()?;
if value < 0 {
Err(EmulationError::ArithmeticOverflow.into())
} else {
Ok(EmValue::I64(value))
}
}
}
fn conv_i_ovf(&self, unsigned_source: bool) -> Result<Self> {
self.conv_i8_ovf(unsigned_source).map(|v| match v {
EmValue::I64(n) => EmValue::NativeInt(n),
other => other,
})
}
fn conv_u_ovf(&self, unsigned_source: bool) -> Result<Self> {
if unsigned_source {
let value = self.to_u64()?;
Ok(EmValue::NativeUInt(value))
} else {
let value = self.to_i64()?;
if value < 0 {
Err(EmulationError::ArithmeticOverflow.into())
} else {
Ok(EmValue::NativeUInt(value as u64))
}
}
}
fn to_i64(&self) -> Result<i64> {
match self {
EmValue::I32(v) => Ok(i64::from(*v)),
EmValue::I64(v) | EmValue::NativeInt(v) => Ok(*v),
EmValue::NativeUInt(v) => Ok(*v as i64),
EmValue::Bool(v) => Ok(i64::from(*v)),
EmValue::Char(v) => Ok(*v as i64),
EmValue::F32(v) => Ok(*v as i64),
EmValue::F64(v) => Ok(*v as i64),
_ => Err(EmulationError::InvalidOperationTypes {
operation: "conversion".to_string(),
operand_types: self.cil_flavor().to_string(),
}
.into()),
}
}
fn to_u64(&self) -> Result<u64> {
match self {
EmValue::I32(v) => Ok(*v as u32 as u64),
EmValue::I64(v) | EmValue::NativeInt(v) => Ok(*v as u64),
EmValue::NativeUInt(v) => Ok(*v),
EmValue::Bool(v) => Ok(u64::from(*v)),
EmValue::Char(v) => Ok(*v as u64),
EmValue::F32(v) => Ok(*v as u64),
EmValue::F64(v) => Ok(*v as u64),
_ => Err(EmulationError::InvalidOperationTypes {
operation: "conversion".to_string(),
operand_types: self.cil_flavor().to_string(),
}
.into()),
}
}
}
#[cfg(test)]
mod tests {
use super::{super::HeapRef, *};
use crate::{metadata::typesystem::PointerSize, Error};
#[test]
fn test_binary_op_add_i32() {
let a = EmValue::I32(10);
let b = EmValue::I32(20);
assert_eq!(
a.binary_op(&b, BinaryOp::Add, PointerSize::Bit64).unwrap(),
EmValue::I32(30)
);
}
#[test]
fn test_binary_op_add_wrapping() {
let a = EmValue::I32(i32::MAX);
let b = EmValue::I32(1);
assert_eq!(
a.binary_op(&b, BinaryOp::Add, PointerSize::Bit64).unwrap(),
EmValue::I32(i32::MIN)
);
}
#[test]
fn test_binary_op_add_i64() {
let a = EmValue::I64(100);
let b = EmValue::I64(200);
assert_eq!(
a.binary_op(&b, BinaryOp::Add, PointerSize::Bit64).unwrap(),
EmValue::I64(300)
);
}
#[test]
fn test_binary_op_add_f64() {
let a = EmValue::F64(1.5);
let b = EmValue::F64(2.5);
assert_eq!(
a.binary_op(&b, BinaryOp::Add, PointerSize::Bit64).unwrap(),
EmValue::F64(4.0)
);
}
#[test]
fn test_binary_op_add_ovf() {
let a = EmValue::I32(i32::MAX);
let b = EmValue::I32(1);
assert!(matches!(
a.binary_op(&b,BinaryOp::AddOvf, PointerSize::Bit64),
Err(Error::Emulation(ref e)) if matches!(e.as_ref(), EmulationError::ArithmeticOverflow)
));
}
#[test]
fn test_binary_op_sub() {
let a = EmValue::I32(30);
let b = EmValue::I32(20);
assert_eq!(
a.binary_op(&b, BinaryOp::Sub, PointerSize::Bit64).unwrap(),
EmValue::I32(10)
);
}
#[test]
fn test_binary_op_mul() {
let a = EmValue::I32(6);
let b = EmValue::I32(7);
assert_eq!(
a.binary_op(&b, BinaryOp::Mul, PointerSize::Bit64).unwrap(),
EmValue::I32(42)
);
}
#[test]
fn test_binary_op_div() {
let a = EmValue::I32(42);
let b = EmValue::I32(6);
assert_eq!(
a.binary_op(&b, BinaryOp::Div, PointerSize::Bit64).unwrap(),
EmValue::I32(7)
);
}
#[test]
fn test_binary_op_div_by_zero() {
let a = EmValue::I32(42);
let b = EmValue::I32(0);
assert!(matches!(
a.binary_op(&b,BinaryOp::Div, PointerSize::Bit64),
Err(Error::Emulation(ref e)) if matches!(e.as_ref(), EmulationError::DivisionByZero)
));
}
#[test]
fn test_binary_op_div_unsigned() {
let a = EmValue::I32(-1); let b = EmValue::I32(2);
let result = a
.binary_op(&b, BinaryOp::DivUn, PointerSize::Bit64)
.unwrap();
assert_eq!(result, EmValue::I32(0x7FFFFFFF));
}
#[test]
fn test_binary_op_rem() {
let a = EmValue::I32(17);
let b = EmValue::I32(5);
assert_eq!(
a.binary_op(&b, BinaryOp::Rem, PointerSize::Bit64).unwrap(),
EmValue::I32(2)
);
}
#[test]
fn test_binary_op_and() {
let a = EmValue::I32(0xFF00);
let b = EmValue::I32(0x0FF0);
assert_eq!(
a.binary_op(&b, BinaryOp::And, PointerSize::Bit64).unwrap(),
EmValue::I32(0x0F00)
);
}
#[test]
fn test_binary_op_or() {
let a = EmValue::I32(0xFF00);
let b = EmValue::I32(0x00FF);
assert_eq!(
a.binary_op(&b, BinaryOp::Or, PointerSize::Bit64).unwrap(),
EmValue::I32(0xFFFF)
);
}
#[test]
fn test_binary_op_xor() {
let a = EmValue::I32(0xFFFF);
let b = EmValue::I32(0x0F0F);
assert_eq!(
a.binary_op(&b, BinaryOp::Xor, PointerSize::Bit64).unwrap(),
EmValue::I32(0xF0F0)
);
}
#[test]
fn test_binary_op_shl() {
let a = EmValue::I32(1);
let b = EmValue::I32(4);
assert_eq!(
a.binary_op(&b, BinaryOp::Shl, PointerSize::Bit64).unwrap(),
EmValue::I32(16)
);
}
#[test]
fn test_binary_op_shr_signed() {
let a = EmValue::I32(-8);
let b = EmValue::I32(2);
assert_eq!(
a.binary_op(&b, BinaryOp::Shr, PointerSize::Bit64).unwrap(),
EmValue::I32(-2)
);
}
#[test]
fn test_binary_op_shr_unsigned() {
let a = EmValue::I32(-8);
let b = EmValue::I32(2);
let result = a
.binary_op(&b, BinaryOp::ShrUn, PointerSize::Bit64)
.unwrap();
assert_eq!(result, EmValue::I32(0x3FFFFFFE_u32 as i32));
}
#[test]
fn test_binary_op_type_mismatch() {
let a = EmValue::I32(1);
let b = EmValue::I64(1);
assert!(matches!(
a.binary_op(&b,BinaryOp::Add, PointerSize::Bit64),
Err(Error::Emulation(ref e)) if matches!(e.as_ref(), EmulationError::InvalidOperationTypes { .. })
));
}
#[test]
fn test_unary_neg_i32() {
let a = EmValue::I32(42);
assert_eq!(
a.unary_op(UnaryOp::Neg, PointerSize::Bit64).unwrap(),
EmValue::I32(-42)
);
}
#[test]
fn test_unary_neg_f64() {
let a = EmValue::F64(std::f64::consts::PI);
assert_eq!(
a.unary_op(UnaryOp::Neg, PointerSize::Bit64).unwrap(),
EmValue::F64(-std::f64::consts::PI)
);
}
#[test]
fn test_unary_not_i32() {
let a = EmValue::I32(0);
assert_eq!(
a.unary_op(UnaryOp::Not, PointerSize::Bit64).unwrap(),
EmValue::I32(-1)
);
}
#[test]
fn test_unary_not_pattern() {
let a = EmValue::I32(0x0F0F0F0F);
assert_eq!(
a.unary_op(UnaryOp::Not, PointerSize::Bit64).unwrap(),
EmValue::I32(0xF0F0F0F0_u32 as i32)
);
}
#[test]
fn test_compare_eq() {
let a = EmValue::I32(42);
let b = EmValue::I32(42);
assert_eq!(a.compare(&b, CompareOp::Eq).unwrap(), EmValue::I32(1));
let c = EmValue::I32(43);
assert_eq!(a.compare(&c, CompareOp::Eq).unwrap(), EmValue::I32(0));
}
#[test]
fn test_compare_ne() {
let a = EmValue::I32(42);
let b = EmValue::I32(43);
assert_eq!(a.compare(&b, CompareOp::Ne).unwrap(), EmValue::I32(1));
}
#[test]
fn test_compare_lt() {
let a = EmValue::I32(10);
let b = EmValue::I32(20);
assert_eq!(a.compare(&b, CompareOp::Lt).unwrap(), EmValue::I32(1));
assert_eq!(b.compare(&a, CompareOp::Lt).unwrap(), EmValue::I32(0));
}
#[test]
fn test_compare_lt_unsigned() {
let a = EmValue::I32(-1); let b = EmValue::I32(1);
assert_eq!(a.compare(&b, CompareOp::Lt).unwrap(), EmValue::I32(1));
assert_eq!(a.compare(&b, CompareOp::LtUn).unwrap(), EmValue::I32(0));
}
#[test]
fn test_compare_gt() {
let a = EmValue::I32(20);
let b = EmValue::I32(10);
assert_eq!(a.compare(&b, CompareOp::Gt).unwrap(), EmValue::I32(1));
}
#[test]
fn test_compare_le() {
let a = EmValue::I32(10);
let b = EmValue::I32(10);
assert_eq!(a.compare(&b, CompareOp::Le).unwrap(), EmValue::I32(1));
}
#[test]
fn test_compare_ge() {
let a = EmValue::I32(10);
let b = EmValue::I32(10);
assert_eq!(a.compare(&b, CompareOp::Ge).unwrap(), EmValue::I32(1));
}
#[test]
fn test_compare_null() {
let a = EmValue::Null;
let b = EmValue::Null;
assert_eq!(a.compare(&b, CompareOp::Eq).unwrap(), EmValue::I32(1));
}
#[test]
fn test_compare_object_ref() {
let a = EmValue::ObjectRef(HeapRef::new(1));
let b = EmValue::ObjectRef(HeapRef::new(1));
let c = EmValue::ObjectRef(HeapRef::new(2));
assert_eq!(a.compare(&b, CompareOp::Eq).unwrap(), EmValue::I32(1));
assert_eq!(a.compare(&c, CompareOp::Eq).unwrap(), EmValue::I32(0));
}
#[test]
fn test_conv_i1() {
let a = EmValue::I32(300);
let result = a.convert(ConversionType::I1, PointerSize::Bit64).unwrap();
assert_eq!(result, EmValue::I32(44));
let b = EmValue::I32(-1);
let result = b.convert(ConversionType::I1, PointerSize::Bit64).unwrap();
assert_eq!(result, EmValue::I32(-1));
}
#[test]
fn test_conv_u1() {
let a = EmValue::I32(-1);
let result = a.convert(ConversionType::U1, PointerSize::Bit64).unwrap();
assert_eq!(result, EmValue::I32(255));
}
#[test]
fn test_conv_i8() {
let a = EmValue::I32(42);
let result = a.convert(ConversionType::I8, PointerSize::Bit64).unwrap();
assert_eq!(result, EmValue::I64(42));
}
#[test]
fn test_conv_u8() {
let a = EmValue::I32(-1);
let result = a.convert(ConversionType::U8, PointerSize::Bit64).unwrap();
assert_eq!(result, EmValue::I64(0xFFFFFFFF));
}
#[test]
fn test_conv_r4() {
let a = EmValue::I32(42);
let result = a.convert(ConversionType::R4, PointerSize::Bit64).unwrap();
assert_eq!(result, EmValue::F32(42.0));
}
#[test]
fn test_conv_r8() {
let a = EmValue::I32(42);
let result = a.convert(ConversionType::R8, PointerSize::Bit64).unwrap();
assert_eq!(result, EmValue::F64(42.0));
}
#[test]
fn test_conv_ovf_i1() {
let a = EmValue::I32(200);
assert!(matches!(
a.convert(ConversionType::I1Ovf, PointerSize::Bit64),
Err(Error::Emulation(ref e)) if matches!(e.as_ref(), EmulationError::ArithmeticOverflow)
));
}
#[test]
fn test_conv_ovf_u1() {
let a = EmValue::I32(-1);
assert!(matches!(
a.convert(ConversionType::U1Ovf, PointerSize::Bit64),
Err(Error::Emulation(ref e)) if matches!(e.as_ref(), EmulationError::ArithmeticOverflow)
));
}
#[test]
fn test_conv_r_un() {
let a = EmValue::I32(-1);
let result = a.convert(ConversionType::RUn, PointerSize::Bit64).unwrap();
assert_eq!(result, EmValue::F64(4294967295.0));
}
#[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");
}
}