#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) enum WasmVal {
I32(u32),
I64(u64),
F32(u32),
F64(u64),
V128(u128),
}
impl WasmVal {
pub(crate) fn is_truthy(self) -> bool {
match self {
WasmVal::I32(i) => i != 0,
_ => panic!("Type error: non-i32 used in boolean-ish context"),
}
}
pub(crate) fn integer_value(self) -> Option<u64> {
match self {
WasmVal::I32(i) => Some(i as u64),
WasmVal::I64(i) => Some(i),
_ => None,
}
}
pub(crate) fn from_bits(ty: waffle::Type, bits: u64) -> Option<Self> {
match ty {
waffle::Type::I32 => Some(WasmVal::I32(bits as u32)),
waffle::Type::I64 => Some(WasmVal::I64(bits)),
waffle::Type::F32 => Some(WasmVal::F32(bits as u32)),
waffle::Type::F64 => Some(WasmVal::F64(bits)),
waffle::Type::V128 => Some(WasmVal::V128(bits as u128)),
waffle::Type::FuncRef | waffle::Type::TypedFuncRef(..) => None,
}
}
}
impl std::convert::TryFrom<waffle::Operator> for WasmVal {
type Error = ();
fn try_from(op: waffle::Operator) -> Result<Self, Self::Error> {
match op {
waffle::Operator::I32Const { value } => Ok(WasmVal::I32(value)),
waffle::Operator::I64Const { value } => Ok(WasmVal::I64(value)),
waffle::Operator::F32Const { value } => Ok(WasmVal::F32(value)),
waffle::Operator::F64Const { value } => Ok(WasmVal::F64(value)),
_ => Err(()),
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) enum AbstractValue {
#[default]
Top,
Concrete(WasmVal),
ConcreteMemory(MemoryBufferIndex, u32),
StaticMemory(u32),
Runtime(Option<waffle::Value>),
}
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct MemoryBufferIndex(pub u32);
impl AbstractValue {
pub(crate) fn meet(a: &AbstractValue, b: &AbstractValue) -> AbstractValue {
match (a, b) {
(AbstractValue::Top, x) | (x, AbstractValue::Top) => x.clone(),
(x, y) if x == y => x.clone(),
(AbstractValue::Concrete(a), AbstractValue::Concrete(b)) if a == b => {
AbstractValue::Concrete(*a)
}
(AbstractValue::Runtime(cause1), AbstractValue::Runtime(cause2)) => {
log::debug!(
"runtime({:?} meet runtime({:?}) -> runtime({:?})",
cause1,
cause2,
cause1.or(*cause2)
);
AbstractValue::Runtime(cause1.or(*cause2))
}
(AbstractValue::Runtime(cause1), _x) | (_x, AbstractValue::Runtime(cause1)) => {
AbstractValue::Runtime(*cause1)
}
(_av1, _av2) => AbstractValue::Runtime(None),
}
}
pub(crate) fn as_const_u32(&self) -> Option<u32> {
match self {
&AbstractValue::Concrete(WasmVal::I32(k)) => Some(k),
&AbstractValue::StaticMemory(addr) => Some(addr),
_ => None,
}
}
pub(crate) fn as_const_u32_or_mem_offset(&self) -> Option<u32> {
match self {
&AbstractValue::Concrete(WasmVal::I32(k)) => Some(k),
&AbstractValue::ConcreteMemory(_, off) => Some(off),
_ => None,
}
}
pub(crate) fn as_const_u64(&self) -> Option<u64> {
match self {
&AbstractValue::Concrete(WasmVal::I64(k)) => Some(k),
&AbstractValue::StaticMemory(addr) => Some(u64::from(addr)),
_ => None,
}
}
pub(crate) fn as_const_truthy(&self) -> Option<bool> {
self.as_const_u32().map(|k| k != 0)
}
}