use crate::{
core::{Typed, TypedVal, UntypedVal},
ir::{Const16, Op, Sign},
Error,
ExternRef,
Func,
Ref,
ValType,
};
use core::num::NonZero;
impl Typed for ExternRef {
const TY: ValType = ValType::ExternRef;
}
macro_rules! impl_typed_for {
( $( $ty:ty as $ident:ident ),* $(,)? ) => {
$(
impl Typed for $ty {
const TY: ValType = crate::ValType::$ident;
}
impl From<TypedVal> for $ty {
fn from(typed_value: TypedVal) -> Self {
debug_assert!(matches!(typed_value.ty(), <$ty as Typed>::TY));
Self::from(typed_value.untyped())
}
}
)*
};
}
impl_typed_for! {
Ref<Func> as FuncRef,
Ref<ExternRef> as ExternRef,
}
pub trait WasmInteger:
Copy
+ Eq
+ Typed
+ From<TypedVal>
+ Into<TypedVal>
+ From<UntypedVal>
+ Into<UntypedVal>
+ TryInto<Const16<Self>>
{
type NonZero: Copy + Into<Self> + TryInto<Const16<Self::NonZero>> + Into<UntypedVal>;
fn non_zero(self) -> Option<Self::NonZero>;
fn is_zero(self) -> bool;
fn wrapping_neg(self) -> Self;
}
macro_rules! impl_wasm_integer {
($($ty:ty),*) => {
$(
impl WasmInteger for $ty {
type NonZero = NonZero<Self>;
fn non_zero(self) -> Option<Self::NonZero> {
Self::NonZero::new(self)
}
fn is_zero(self) -> bool {
self == 0
}
fn wrapping_neg(self) -> Self {
Self::wrapping_neg(self)
}
}
)*
};
}
impl_wasm_integer!(i32, u32, i64, u64);
pub trait WasmFloat: Typed + Copy + Into<TypedVal> + From<TypedVal> {
fn sign(self) -> Sign<Self>;
}
impl WasmFloat for f32 {
fn sign(self) -> Sign<Self> {
Sign::from(self)
}
}
impl WasmFloat for f64 {
fn sign(self) -> Sign<Self> {
Sign::from(self)
}
}
pub trait Wrap<T> {
fn wrap(self) -> T;
}
impl<T> Wrap<T> for T {
#[inline]
fn wrap(self) -> T {
self
}
}
macro_rules! impl_wrap_for {
( $($from_ty:ty => $to_ty:ty),* $(,)? ) => {
$(
impl Wrap<$to_ty> for $from_ty {
#[inline]
fn wrap(self) -> $to_ty { self as _ }
}
)*
};
}
impl_wrap_for! {
i16 => i8,
i32 => i8,
i32 => i16,
i64 => i8,
i64 => i16,
i64 => i32,
u16 => u8,
u32 => u8,
u32 => u16,
u64 => u8,
u64 => u16,
u64 => u32,
}
pub trait BumpFuelConsumption {
fn bump_fuel_consumption(&mut self, delta: u64) -> Result<(), Error>;
}
impl BumpFuelConsumption for Op {
fn bump_fuel_consumption(&mut self, delta: u64) -> Result<(), Error> {
match self {
Self::ConsumeFuel { block_fuel } => block_fuel.bump_by(delta).map_err(Error::from),
instr => panic!("expected `Op::ConsumeFuel` but found: {instr:?}"),
}
}
}
pub trait IsInstructionParameter {
fn is_instruction_parameter(&self) -> bool;
}
impl IsInstructionParameter for Op {
#[rustfmt::skip]
fn is_instruction_parameter(&self) -> bool {
matches!(self,
| Self::TableIndex { .. }
| Self::MemoryIndex { .. }
| Self::DataIndex { .. }
| Self::ElemIndex { .. }
| Self::Const32 { .. }
| Self::I64Const32 { .. }
| Self::F64Const32 { .. }
| Self::BranchTableTarget { .. }
| Self::Imm16AndImm32 { .. }
| Self::SlotAndImm32 { .. }
| Self::SlotSpan { .. }
| Self::Slot { .. }
| Self::Slot2 { .. }
| Self::Slot3 { .. }
| Self::SlotList { .. }
| Self::CallIndirectParams { .. }
| Self::CallIndirectParamsImm16 { .. }
)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Instr(u32);
impl From<u32> for Instr {
fn from(index: u32) -> Self {
Self(index)
}
}
impl From<Instr> for u32 {
fn from(instr: Instr) -> Self {
instr.0
}
}
impl Instr {
pub fn from_usize(value: usize) -> Self {
let Ok(index) = u32::try_from(value) else {
panic!("out of bounds index {value} for `Instr`")
};
Self(index)
}
pub fn into_usize(self) -> usize {
match usize::try_from(self.0) {
Ok(index) => index,
Err(error) => {
panic!("out of bound index {} for `Instr`: {error}", self.0)
}
}
}
pub fn distance(self, other: Self) -> u32 {
self.0.abs_diff(other.0)
}
}