use analyssa::ir::value::ConstValue as AnalyssaConstValue;
use crate::{
analysis::ssa::{target::CilTarget, types::SsaType},
assembly::Immediate,
CilObject, Error,
};
pub type ConstValue<T = CilTarget> = AnalyssaConstValue<T>;
pub type AbstractValue<T = CilTarget> = analyssa::ir::value::AbstractValue<T>;
pub trait ConstValueCilExt {
fn ssa_type(&self) -> SsaType;
fn as_string_content(&self, assembly: &CilObject) -> Option<String>;
}
impl ConstValueCilExt for AnalyssaConstValue<CilTarget> {
fn ssa_type(&self) -> SsaType {
match self {
AnalyssaConstValue::I8(_) => SsaType::I8,
AnalyssaConstValue::I16(_) => SsaType::I16,
AnalyssaConstValue::I32(_) => SsaType::I32,
AnalyssaConstValue::I64(_) => SsaType::I64,
AnalyssaConstValue::U8(_) => SsaType::U8,
AnalyssaConstValue::U16(_) => SsaType::U16,
AnalyssaConstValue::U32(_) => SsaType::U32,
AnalyssaConstValue::U64(_) => SsaType::U64,
AnalyssaConstValue::F32(_) => SsaType::F32,
AnalyssaConstValue::F64(_) => SsaType::F64,
AnalyssaConstValue::NativeInt(_)
| AnalyssaConstValue::Type(_)
| AnalyssaConstValue::MethodHandle(_)
| AnalyssaConstValue::FieldHandle(_) => SsaType::NativeInt,
AnalyssaConstValue::NativeUInt(_) => SsaType::NativeUInt,
AnalyssaConstValue::True | AnalyssaConstValue::False => SsaType::Bool,
AnalyssaConstValue::Null
| AnalyssaConstValue::String(_)
| AnalyssaConstValue::DecryptedString(_)
| AnalyssaConstValue::Vector(_)
| AnalyssaConstValue::DecryptedArray { .. } => SsaType::Object,
}
}
fn as_string_content(&self, assembly: &CilObject) -> Option<String> {
match self {
AnalyssaConstValue::DecryptedString(s) => Some(s.to_string()),
AnalyssaConstValue::String(idx) => assembly
.userstrings()
.and_then(|us| us.get(*idx as usize).ok())
.map(|s| s.to_string_lossy()),
_ => None,
}
}
}
impl TryFrom<&AnalyssaConstValue<CilTarget>> for Immediate {
type Error = Error;
#[allow(clippy::cast_possible_wrap)] fn try_from(value: &AnalyssaConstValue<CilTarget>) -> Result<Self, Self::Error> {
match value {
AnalyssaConstValue::I8(v) => Ok(Immediate::Int8(*v)),
AnalyssaConstValue::I16(v) => Ok(Immediate::Int16(*v)),
AnalyssaConstValue::I32(v) => Ok(Immediate::Int32(*v)),
AnalyssaConstValue::U8(v) => Ok(Immediate::Int8(*v as i8)),
AnalyssaConstValue::U16(v) => Ok(Immediate::Int16(*v as i16)),
AnalyssaConstValue::U32(v) => Ok(Immediate::Int32(*v as i32)),
AnalyssaConstValue::I64(v) | AnalyssaConstValue::NativeInt(v) => {
Ok(Immediate::Int64(*v))
}
AnalyssaConstValue::U64(v) | AnalyssaConstValue::NativeUInt(v) => {
Ok(Immediate::Int64(*v as i64))
}
AnalyssaConstValue::F32(v) => Ok(Immediate::Float32(*v)),
AnalyssaConstValue::F64(v) => Ok(Immediate::Float64(*v)),
AnalyssaConstValue::True => Ok(Immediate::Int32(1)),
AnalyssaConstValue::False => Ok(Immediate::Int32(0)),
AnalyssaConstValue::String(_)
| AnalyssaConstValue::DecryptedString(_)
| AnalyssaConstValue::Vector(_)
| AnalyssaConstValue::DecryptedArray { .. }
| AnalyssaConstValue::Null
| AnalyssaConstValue::Type(_)
| AnalyssaConstValue::MethodHandle(_)
| AnalyssaConstValue::FieldHandle(_) => Err(Error::SsaError(format!(
"Cannot convert {value:?} to Immediate - use pattern matching to handle this case"
))),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use analyssa::ir::value::{ComputedOp, ComputedValue};
use analyssa::ir::variable::SsaVarId;
use crate::metadata::typesystem::PointerSize;
type ConstValue = super::ConstValue<CilTarget>;
type AbstractValue = super::AbstractValue<CilTarget>;
#[test]
fn test_const_arithmetic() {
let a = ConstValue::I32(10);
let b = ConstValue::I32(3);
assert_eq!(a.add(&b, PointerSize::Bit64), Some(ConstValue::I32(13)));
assert_eq!(a.sub(&b, PointerSize::Bit64), Some(ConstValue::I32(7)));
assert_eq!(a.mul(&b, PointerSize::Bit64), Some(ConstValue::I32(30)));
}
#[test]
fn test_const_comparison() {
let a = ConstValue::I32(10);
let b = ConstValue::I32(3);
let c = ConstValue::I32(10);
assert_eq!(a.ceq(&b), Some(ConstValue::False));
assert_eq!(a.ceq(&c), Some(ConstValue::True));
assert_eq!(a.clt(&b), Some(ConstValue::False));
assert_eq!(b.clt(&a), Some(ConstValue::True));
}
#[test]
fn test_const_bool_conversion() {
assert_eq!(ConstValue::True.as_bool(), Some(true));
assert_eq!(ConstValue::False.as_bool(), Some(false));
assert_eq!(ConstValue::I32(0).as_bool(), Some(false));
assert_eq!(ConstValue::I32(42).as_bool(), Some(true));
assert_eq!(ConstValue::Null.as_bool(), Some(false));
}
#[test]
fn test_abstract_value_meet() {
assert_eq!(
AbstractValue::Top.meet(&AbstractValue::Constant(ConstValue::I32(5))),
AbstractValue::Constant(ConstValue::I32(5))
);
assert_eq!(
AbstractValue::Constant(ConstValue::I32(5))
.meet(&AbstractValue::Constant(ConstValue::I32(5))),
AbstractValue::Constant(ConstValue::I32(5))
);
assert_eq!(
AbstractValue::Constant(ConstValue::I32(5))
.meet(&AbstractValue::Constant(ConstValue::I32(10))),
AbstractValue::Bottom
);
assert_eq!(
AbstractValue::Bottom.meet(&AbstractValue::Constant(ConstValue::I32(5))),
AbstractValue::Bottom
);
}
#[test]
fn test_range_merge() {
let r1 = AbstractValue::Range { min: 0, max: 10 };
let r2 = AbstractValue::Range { min: 5, max: 15 };
assert_eq!(r1.meet(&r2), AbstractValue::Range { min: 0, max: 15 });
}
#[test]
fn test_computed_value_normalization() {
let v0 = SsaVarId::from_index(0);
let v1 = SsaVarId::from_index(1);
let cv1 = ComputedValue::binary(ComputedOp::Add, v1, v0).normalized();
let cv2 = ComputedValue::binary(ComputedOp::Add, v0, v1).normalized();
assert_eq!(cv1, cv2);
let cv3 = ComputedValue::binary(ComputedOp::Sub, v1, v0).normalized();
let cv4 = ComputedValue::binary(ComputedOp::Sub, v0, v1).normalized();
assert_ne!(cv3, cv4);
}
#[test]
fn test_is_zero() {
assert!(ConstValue::I8(0).is_zero());
assert!(ConstValue::I16(0).is_zero());
assert!(ConstValue::I32(0).is_zero());
assert!(ConstValue::I64(0).is_zero());
assert!(ConstValue::U8(0).is_zero());
assert!(ConstValue::U16(0).is_zero());
assert!(ConstValue::U32(0).is_zero());
assert!(ConstValue::U64(0).is_zero());
assert!(ConstValue::NativeInt(0).is_zero());
assert!(ConstValue::NativeUInt(0).is_zero());
assert!(ConstValue::False.is_zero());
assert!(!ConstValue::I32(1).is_zero());
assert!(!ConstValue::I32(-1).is_zero());
assert!(!ConstValue::True.is_zero());
assert!(!ConstValue::Null.is_zero()); }
#[test]
fn test_is_one() {
assert!(ConstValue::I8(1).is_one());
assert!(ConstValue::I16(1).is_one());
assert!(ConstValue::I32(1).is_one());
assert!(ConstValue::I64(1).is_one());
assert!(ConstValue::U8(1).is_one());
assert!(ConstValue::U16(1).is_one());
assert!(ConstValue::U32(1).is_one());
assert!(ConstValue::U64(1).is_one());
assert!(ConstValue::NativeInt(1).is_one());
assert!(ConstValue::NativeUInt(1).is_one());
assert!(ConstValue::True.is_one());
assert!(!ConstValue::I32(0).is_one());
assert!(!ConstValue::I32(2).is_one());
assert!(!ConstValue::False.is_one());
}
#[test]
fn test_is_minus_one() {
assert!(ConstValue::I8(-1).is_minus_one());
assert!(ConstValue::I16(-1).is_minus_one());
assert!(ConstValue::I32(-1).is_minus_one());
assert!(ConstValue::I64(-1).is_minus_one());
assert!(ConstValue::NativeInt(-1).is_minus_one());
assert!(!ConstValue::I32(0).is_minus_one());
assert!(!ConstValue::I32(1).is_minus_one());
assert!(!ConstValue::U32(u32::MAX).is_minus_one());
}
#[test]
fn test_convert_to_widening() {
let v = ConstValue::I32(42);
assert_eq!(
v.convert_to(&SsaType::I64, false, 8),
Some(ConstValue::I64(42))
);
let v = ConstValue::I32(-42);
assert_eq!(
v.convert_to(&SsaType::I64, false, 8),
Some(ConstValue::I64(-42))
);
let v = ConstValue::U32(42);
assert_eq!(
v.convert_to(&SsaType::U64, false, 8),
Some(ConstValue::U64(42))
);
}
#[test]
fn test_convert_to_narrowing() {
let v = ConstValue::I32(42);
assert_eq!(
v.convert_to(&SsaType::I8, false, 8),
Some(ConstValue::I8(42))
);
let v = ConstValue::I32(1000);
assert_eq!(
v.convert_to(&SsaType::I8, false, 8),
Some(ConstValue::I8(-24))
);
let v = ConstValue::I64(0x1_0000_0042);
assert_eq!(
v.convert_to(&SsaType::I32, false, 8),
Some(ConstValue::I32(0x42))
);
}
#[test]
fn test_convert_to_float() {
let v = ConstValue::I32(42);
assert_eq!(
v.convert_to(&SsaType::F32, false, 8),
Some(ConstValue::F32(42.0))
);
let v = ConstValue::I32(42);
assert_eq!(
v.convert_to(&SsaType::F64, false, 8),
Some(ConstValue::F64(42.0))
);
let v = ConstValue::U32(42);
assert_eq!(
v.convert_to(&SsaType::F32, true, 8),
Some(ConstValue::F32(42.0))
);
}
#[test]
fn test_convert_to_bool() {
let v = ConstValue::I32(42);
assert_eq!(
v.convert_to(&SsaType::Bool, false, 8),
Some(ConstValue::True)
);
let v = ConstValue::I32(0);
assert_eq!(
v.convert_to(&SsaType::Bool, false, 8),
Some(ConstValue::False)
);
}
#[test]
fn test_convert_to_checked_in_range() {
let v = ConstValue::I32(100);
assert_eq!(
v.convert_to_checked(&SsaType::I8, false, 8),
Some(ConstValue::I8(100))
);
let v = ConstValue::I32(127);
assert_eq!(
v.convert_to_checked(&SsaType::I8, false, 8),
Some(ConstValue::I8(127))
);
let v = ConstValue::I32(-128);
assert_eq!(
v.convert_to_checked(&SsaType::I8, false, 8),
Some(ConstValue::I8(-128))
);
}
#[test]
fn test_convert_to_checked_overflow() {
let v = ConstValue::I32(1000);
assert_eq!(v.convert_to_checked(&SsaType::I8, false, 8), None);
let v = ConstValue::I32(-1);
assert_eq!(v.convert_to_checked(&SsaType::U8, false, 8), None);
assert_eq!(v.convert_to_checked(&SsaType::U32, false, 8), None);
assert_eq!(v.convert_to_checked(&SsaType::U64, false, 8), None);
}
#[test]
fn test_convert_to_char() {
let v = ConstValue::I32(65); assert_eq!(
v.convert_to(&SsaType::Char, false, 8),
Some(ConstValue::U16(65))
);
}
#[test]
fn test_convert_to_native() {
let v = ConstValue::I32(42);
assert_eq!(
v.convert_to(&SsaType::NativeInt, false, 8),
Some(ConstValue::NativeInt(42))
);
let v = ConstValue::I32(42);
assert_eq!(
v.convert_to(&SsaType::NativeUInt, false, 8),
Some(ConstValue::NativeUInt(42))
);
}
#[test]
fn test_const_div() {
let a = ConstValue::I32(100);
let b = ConstValue::I32(7);
assert_eq!(a.div(&b, PointerSize::Bit64), Some(ConstValue::I32(14)));
let a = ConstValue::U32(100);
let b = ConstValue::U32(7);
assert_eq!(a.div(&b, PointerSize::Bit64), Some(ConstValue::U32(14)));
let a = ConstValue::I64(1000);
let b = ConstValue::I64(33);
assert_eq!(a.div(&b, PointerSize::Bit64), Some(ConstValue::I64(30)));
}
#[test]
fn test_const_div_by_zero() {
let a = ConstValue::I32(100);
let zero = ConstValue::I32(0);
assert_eq!(a.div(&zero, PointerSize::Bit64), None);
let a = ConstValue::U64(100);
let zero = ConstValue::U64(0);
assert_eq!(a.div(&zero, PointerSize::Bit64), None);
}
#[test]
fn test_const_div_negative() {
let a = ConstValue::I32(-100);
let b = ConstValue::I32(7);
assert_eq!(a.div(&b, PointerSize::Bit64), Some(ConstValue::I32(-14))); }
#[test]
fn test_const_rem() {
let a = ConstValue::I32(100);
let b = ConstValue::I32(7);
assert_eq!(a.rem(&b, PointerSize::Bit64), Some(ConstValue::I32(2)));
let a = ConstValue::U32(100);
let b = ConstValue::U32(7);
assert_eq!(a.rem(&b, PointerSize::Bit64), Some(ConstValue::U32(2)));
}
#[test]
fn test_const_rem_by_zero() {
let a = ConstValue::I32(100);
let zero = ConstValue::I32(0);
assert_eq!(a.rem(&zero, PointerSize::Bit64), None);
}
#[test]
fn test_const_rem_negative() {
let a = ConstValue::I32(-100);
let b = ConstValue::I32(7);
assert_eq!(a.rem(&b, PointerSize::Bit64), Some(ConstValue::I32(-2)));
}
#[test]
fn test_const_shl() {
let a = ConstValue::I32(1);
let shift = ConstValue::I32(4);
assert_eq!(a.shl(&shift, PointerSize::Bit64), Some(ConstValue::I32(16)));
let a = ConstValue::I32(1);
let shift = ConstValue::I32(31);
assert_eq!(
a.shl(&shift, PointerSize::Bit64),
Some(ConstValue::I32(i32::MIN))
); }
#[test]
fn test_const_shr_signed() {
let a = ConstValue::I32(-16);
let shift = ConstValue::I32(2);
assert_eq!(
a.shr(&shift, false, PointerSize::Bit64),
Some(ConstValue::I32(-4))
);
let b = ConstValue::I32(16);
let shift = ConstValue::I32(2);
assert_eq!(
b.shr(&shift, false, PointerSize::Bit64),
Some(ConstValue::I32(4))
);
}
#[test]
fn test_const_shr_unsigned() {
let a = ConstValue::I32(-1); let shift = ConstValue::I32(1);
let result = a.shr(&shift, true, PointerSize::Bit64);
assert!(result.is_some());
if let Some(ConstValue::I32(v)) = result {
assert_eq!(v, 0x7FFFFFFF);
}
}
#[test]
fn test_const_shr_i64() {
let a = ConstValue::I64(256);
let shift = ConstValue::I32(4);
assert_eq!(
a.shr(&shift, false, PointerSize::Bit64),
Some(ConstValue::I64(16))
);
}
#[test]
fn test_const_cgt() {
let a = ConstValue::I32(10);
let b = ConstValue::I32(5);
assert_eq!(a.cgt(&b), Some(ConstValue::True));
assert_eq!(b.cgt(&a), Some(ConstValue::False));
assert_eq!(a.cgt(&a), Some(ConstValue::False)); }
#[test]
fn test_const_cgt_i64() {
let a = ConstValue::I64(1000);
let b = ConstValue::I64(500);
assert_eq!(a.cgt(&b), Some(ConstValue::True));
}
#[test]
fn test_const_clt_negative() {
let a = ConstValue::I32(-10);
let b = ConstValue::I32(5);
assert_eq!(a.clt(&b), Some(ConstValue::True)); }
#[test]
fn test_const_float_arithmetic() {
let a = ConstValue::F32(10.5);
let b = ConstValue::F32(2.5);
assert_eq!(a.add(&b, PointerSize::Bit64), Some(ConstValue::F32(13.0)));
assert_eq!(a.sub(&b, PointerSize::Bit64), Some(ConstValue::F32(8.0)));
assert_eq!(a.mul(&b, PointerSize::Bit64), Some(ConstValue::F32(26.25)));
let a = ConstValue::F64(100.0);
let b = ConstValue::F64(4.0);
assert_eq!(a.div(&b, PointerSize::Bit64), Some(ConstValue::F64(25.0)));
}
#[test]
fn test_const_float_comparison() {
let a = ConstValue::F64(std::f64::consts::PI);
let b = ConstValue::F64(std::f64::consts::E);
assert_eq!(a.cgt(&b), Some(ConstValue::True));
assert_eq!(a.clt(&b), Some(ConstValue::False));
assert_eq!(a.ceq(&a), Some(ConstValue::True));
}
#[test]
fn test_const_float_div_by_zero() {
let a = ConstValue::F64(1.0);
let b = ConstValue::F64(0.0);
let result = a.div(&b, PointerSize::Bit64);
assert!(result.is_some());
if let Some(ConstValue::F64(v)) = result {
assert!(v.is_infinite());
}
}
#[test]
fn test_is_zero_integer_types() {
assert!(ConstValue::I8(0).is_zero());
assert!(ConstValue::I16(0).is_zero());
assert!(ConstValue::I32(0).is_zero());
assert!(ConstValue::I64(0).is_zero());
assert!(ConstValue::U8(0).is_zero());
assert!(ConstValue::U16(0).is_zero());
assert!(ConstValue::U32(0).is_zero());
assert!(ConstValue::U64(0).is_zero());
assert!(ConstValue::NativeInt(0).is_zero());
assert!(ConstValue::NativeUInt(0).is_zero());
assert!(ConstValue::False.is_zero());
assert!(!ConstValue::I32(1).is_zero());
assert!(!ConstValue::True.is_zero());
}
#[test]
fn test_is_one_integer_types() {
assert!(ConstValue::I8(1).is_one());
assert!(ConstValue::I16(1).is_one());
assert!(ConstValue::I32(1).is_one());
assert!(ConstValue::I64(1).is_one());
assert!(ConstValue::U8(1).is_one());
assert!(ConstValue::U16(1).is_one());
assert!(ConstValue::U32(1).is_one());
assert!(ConstValue::U64(1).is_one());
assert!(ConstValue::NativeInt(1).is_one());
assert!(ConstValue::NativeUInt(1).is_one());
assert!(ConstValue::True.is_one());
assert!(!ConstValue::I32(0).is_one());
assert!(!ConstValue::False.is_one());
}
#[test]
fn test_is_minus_one_signed_types() {
assert!(ConstValue::I8(-1).is_minus_one());
assert!(ConstValue::I16(-1).is_minus_one());
assert!(ConstValue::I32(-1).is_minus_one());
assert!(ConstValue::I64(-1).is_minus_one());
assert!(ConstValue::NativeInt(-1).is_minus_one());
assert!(!ConstValue::U8(255).is_minus_one());
assert!(!ConstValue::U32(0xFFFFFFFF).is_minus_one());
}
#[test]
fn test_zero_of_same_type_preserves_type() {
assert_eq!(ConstValue::I8(42).zero_of_same_type(), ConstValue::I8(0));
assert_eq!(ConstValue::I16(42).zero_of_same_type(), ConstValue::I16(0));
assert_eq!(ConstValue::I32(42).zero_of_same_type(), ConstValue::I32(0));
assert_eq!(ConstValue::I64(42).zero_of_same_type(), ConstValue::I64(0));
assert_eq!(ConstValue::U8(42).zero_of_same_type(), ConstValue::U8(0));
assert_eq!(ConstValue::U16(42).zero_of_same_type(), ConstValue::U16(0));
assert_eq!(ConstValue::U32(42).zero_of_same_type(), ConstValue::U32(0));
assert_eq!(ConstValue::U64(42).zero_of_same_type(), ConstValue::U64(0));
assert_eq!(
ConstValue::NativeInt(42).zero_of_same_type(),
ConstValue::NativeInt(0)
);
assert_eq!(
ConstValue::NativeUInt(42).zero_of_same_type(),
ConstValue::NativeUInt(0)
);
assert_eq!(
ConstValue::F32(std::f32::consts::PI).zero_of_same_type(),
ConstValue::F32(0.0)
);
assert_eq!(
ConstValue::F64(std::f64::consts::PI).zero_of_same_type(),
ConstValue::F64(0.0)
);
assert_eq!(ConstValue::Null.zero_of_same_type(), ConstValue::I32(0));
assert_eq!(ConstValue::True.zero_of_same_type(), ConstValue::I32(0));
}
#[test]
fn test_abstract_value_join() {
let top = AbstractValue::Top;
let bottom = AbstractValue::Bottom;
let const_a = AbstractValue::Constant(ConstValue::I32(42));
let const_b = AbstractValue::Constant(ConstValue::I32(42));
let const_c = AbstractValue::Constant(ConstValue::I32(99));
assert_eq!(top.join(&const_a), AbstractValue::Top);
assert_eq!(const_a.join(&top), AbstractValue::Top);
assert_eq!(bottom.join(&const_a), const_a);
assert_eq!(const_a.join(&bottom), const_a);
assert_eq!(const_a.join(&const_b), const_a);
assert_eq!(const_a.join(&const_c), AbstractValue::Top);
}
#[test]
fn test_abstract_value_is_constant() {
let top = AbstractValue::Top;
let bottom = AbstractValue::Bottom;
let const_val = AbstractValue::Constant(ConstValue::I32(42));
assert!(!top.is_constant());
assert!(!bottom.is_constant());
assert!(const_val.is_constant());
}
#[test]
fn test_abstract_value_as_constant() {
let const_val = AbstractValue::Constant(ConstValue::I32(42));
assert_eq!(const_val.as_constant(), Some(&ConstValue::I32(42)));
let top = AbstractValue::Top;
assert_eq!(top.as_constant(), None);
}
#[test]
fn test_as_i64() {
assert_eq!(ConstValue::I8(-100).as_i64(), Some(-100));
assert_eq!(ConstValue::I16(-30000).as_i64(), Some(-30000));
assert_eq!(
ConstValue::I32(-2_000_000_000).as_i64(),
Some(-2_000_000_000)
);
assert_eq!(
ConstValue::I64(-9_000_000_000_000).as_i64(),
Some(-9_000_000_000_000)
);
assert_eq!(ConstValue::NativeInt(42).as_i64(), Some(42));
assert_eq!(ConstValue::U8(200).as_i64(), Some(200));
assert_eq!(ConstValue::U16(60000).as_i64(), Some(60000));
assert_eq!(ConstValue::U32(4_000_000_000).as_i64(), Some(4_000_000_000));
assert_eq!(ConstValue::U64(1_000_000).as_i64(), None);
assert_eq!(ConstValue::True.as_i64(), Some(1));
assert_eq!(ConstValue::False.as_i64(), Some(0));
}
#[test]
fn test_as_u64() {
assert_eq!(ConstValue::U8(200).as_u64(), Some(200));
assert_eq!(ConstValue::U16(60000).as_u64(), Some(60000));
assert_eq!(ConstValue::U32(4_000_000_000).as_u64(), Some(4_000_000_000));
assert_eq!(ConstValue::U64(u64::MAX).as_u64(), Some(u64::MAX));
assert_eq!(ConstValue::I32(100).as_u64(), Some(100));
assert_eq!(ConstValue::I32(-1).as_u64(), None);
}
#[test]
fn test_cross_type_arithmetic() {
let i32_val = ConstValue::I32(10);
let i64_val = ConstValue::I64(20);
assert_eq!(
i32_val.add(&i64_val, PointerSize::Bit64),
Some(ConstValue::I64(30))
);
let u32_val = ConstValue::U32(100);
let u64_val = ConstValue::U64(200);
assert_eq!(
u32_val.add(&u64_val, PointerSize::Bit64),
Some(ConstValue::U64(300))
);
}
#[test]
fn test_incompatible_type_arithmetic() {
let i32_val = ConstValue::I32(10);
let f32_val = ConstValue::F32(std::f32::consts::PI);
assert!(i32_val.add(&f32_val, PointerSize::Bit64).is_none());
let u64_val = ConstValue::U64(100);
let i64_val = ConstValue::I64(200);
assert!(u64_val.add(&i64_val, PointerSize::Bit64).is_none());
}
#[test]
fn test_null_const_value() {
let null = ConstValue::Null;
assert!(null.is_null());
assert!(!null.is_one());
assert!(!null.is_bool());
}
#[test]
fn test_string_const_value() {
let s = ConstValue::String(123);
assert!(!s.is_zero());
assert!(!s.is_one());
}
#[test]
fn test_bool_const_values() {
let t = ConstValue::True;
let f = ConstValue::False;
assert!(t.is_bool());
assert!(f.is_bool());
assert!(t.is_one());
assert!(f.is_zero());
}
}