wasm/execution/
value.rs

1use core::fmt::{Debug, Display};
2use core::ops::{Add, Div, Mul, Sub};
3use core::{f32, f64};
4
5use crate::core::reader::types::{NumType, ValType};
6use crate::RefType;
7
8#[derive(Clone, Debug, Copy, PartialOrd)]
9#[repr(transparent)]
10pub struct F32(pub f32);
11
12impl Display for F32 {
13    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
14        write!(f, "{}", self.0)
15    }
16}
17
18impl PartialEq for F32 {
19    fn eq(&self, other: &Self) -> bool {
20        self.0.eq(&other.0)
21    }
22}
23
24impl Add for F32 {
25    type Output = Self;
26    fn add(self, rhs: Self) -> Self::Output {
27        Self(self.0 + rhs.0)
28    }
29}
30
31impl Sub for F32 {
32    type Output = Self;
33    fn sub(self, rhs: Self) -> Self::Output {
34        Self(self.0 - rhs.0)
35    }
36}
37
38impl Mul for F32 {
39    type Output = Self;
40    fn mul(self, rhs: Self) -> Self::Output {
41        Self(self.0 * rhs.0)
42    }
43}
44
45impl Div for F32 {
46    type Output = Self;
47    fn div(self, rhs: Self) -> Self::Output {
48        Self(self.0 / rhs.0)
49    }
50}
51
52impl F32 {
53    pub fn abs(&self) -> Self {
54        Self(libm::fabsf(self.0))
55    }
56    pub fn neg(&self) -> Self {
57        Self(-self.0)
58    }
59    pub fn ceil(&self) -> Self {
60        if self.0.is_nan() {
61            return Self(f32::NAN);
62        }
63        Self(libm::ceilf(self.0))
64    }
65    pub fn floor(&self) -> Self {
66        if self.0.is_nan() {
67            return Self(f32::NAN);
68        }
69        Self(libm::floorf(self.0))
70    }
71    pub fn trunc(&self) -> Self {
72        if self.0.is_nan() {
73            return Self(f32::NAN);
74        }
75        Self(libm::truncf(self.0))
76    }
77    pub fn nearest(&self) -> Self {
78        if self.0.is_nan() {
79            return Self(f32::NAN);
80        }
81        Self(libm::rintf(self.0))
82    }
83    pub fn round(&self) -> Self {
84        Self(libm::roundf(self.0))
85    }
86    pub fn sqrt(&self) -> Self {
87        Self(libm::sqrtf(self.0))
88    }
89
90    pub fn min(&self, rhs: Self) -> Self {
91        Self(if self.0.is_nan() || rhs.0.is_nan() {
92            f32::NAN
93        } else if self.0 == 0.0 && rhs.0 == 0.0 {
94            if self.to_bits() >> 31 == 1 {
95                self.0
96            } else {
97                rhs.0
98            }
99        } else {
100            self.0.min(rhs.0)
101        })
102    }
103    pub fn max(&self, rhs: Self) -> Self {
104        Self(if self.0.is_nan() || rhs.0.is_nan() {
105            f32::NAN
106        } else if self.0 == 0.0 && rhs.0 == 0.0 {
107            if self.to_bits() >> 31 == 1 {
108                rhs.0
109            } else {
110                self.0
111            }
112        } else {
113            self.0.max(rhs.0)
114        })
115    }
116    pub fn copysign(&self, rhs: Self) -> Self {
117        Self(libm::copysignf(self.0, rhs.0))
118    }
119    pub fn from_bits(other: u32) -> Self {
120        Self(f32::from_bits(other))
121    }
122    pub fn is_nan(&self) -> bool {
123        self.0.is_nan()
124    }
125    pub fn is_infinity(&self) -> bool {
126        self.0.is_infinite()
127    }
128    pub fn is_negative_infinity(&self) -> bool {
129        self.0.is_infinite() && self.0 < 0.0
130    }
131
132    pub fn as_i32(&self) -> i32 {
133        self.0 as i32
134    }
135    pub fn as_u32(&self) -> u32 {
136        self.0 as u32
137    }
138    pub fn as_i64(&self) -> i64 {
139        self.0 as i64
140    }
141    pub fn as_u64(&self) -> u64 {
142        self.0 as u64
143    }
144    pub fn as_f64(&self) -> F64 {
145        F64(self.0 as f64)
146    }
147    pub fn reinterpret_as_i32(&self) -> i32 {
148        self.0.to_bits() as i32
149    }
150    pub fn to_bits(&self) -> u32 {
151        self.0.to_bits()
152    }
153}
154
155#[derive(Clone, Debug, Copy, PartialOrd)]
156#[repr(transparent)]
157pub struct F64(pub f64);
158
159impl Display for F64 {
160    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
161        write!(f, "{}", self.0)
162    }
163}
164
165impl PartialEq for F64 {
166    fn eq(&self, other: &Self) -> bool {
167        self.0.eq(&other.0)
168    }
169}
170
171impl Add for F64 {
172    type Output = Self;
173    fn add(self, rhs: Self) -> Self::Output {
174        Self(self.0 + rhs.0)
175    }
176}
177
178impl Sub for F64 {
179    type Output = Self;
180    fn sub(self, rhs: Self) -> Self::Output {
181        Self(self.0 - rhs.0)
182    }
183}
184
185impl Mul for F64 {
186    type Output = Self;
187    fn mul(self, rhs: Self) -> Self::Output {
188        Self(self.0 * rhs.0)
189    }
190}
191
192impl Div for F64 {
193    type Output = Self;
194    fn div(self, rhs: Self) -> Self::Output {
195        Self(self.0 / rhs.0)
196    }
197}
198
199impl F64 {
200    pub fn abs(&self) -> Self {
201        Self(libm::fabs(self.0))
202    }
203    pub fn neg(&self) -> Self {
204        Self(-self.0)
205    }
206    pub fn ceil(&self) -> Self {
207        if self.0.is_nan() {
208            return Self(f64::NAN);
209        }
210        Self(libm::ceil(self.0))
211    }
212    pub fn floor(&self) -> Self {
213        if self.0.is_nan() {
214            return Self(f64::NAN);
215        }
216        Self(libm::floor(self.0))
217    }
218    pub fn trunc(&self) -> Self {
219        if self.0.is_nan() {
220            return Self(f64::NAN);
221        }
222        Self(libm::trunc(self.0))
223    }
224    pub fn nearest(&self) -> Self {
225        if self.0.is_nan() {
226            return Self(f64::NAN);
227        }
228        Self(libm::rint(self.0))
229    }
230    pub fn round(&self) -> Self {
231        Self(libm::round(self.0))
232    }
233    pub fn sqrt(&self) -> Self {
234        Self(libm::sqrt(self.0))
235    }
236
237    pub fn min(&self, rhs: Self) -> Self {
238        Self(if self.0.is_nan() || rhs.0.is_nan() {
239            f64::NAN
240        } else if self.0 == 0.0 && rhs.0 == 0.0 {
241            if self.to_bits() >> 63 == 1 {
242                self.0
243            } else {
244                rhs.0
245            }
246        } else {
247            self.0.min(rhs.0)
248        })
249    }
250    pub fn max(&self, rhs: Self) -> Self {
251        Self(if self.0.is_nan() || rhs.0.is_nan() {
252            f64::NAN
253        } else if self.0 == 0.0 && rhs.0 == 0.0 {
254            if self.to_bits() >> 63 == 1 {
255                rhs.0
256            } else {
257                self.0
258            }
259        } else {
260            self.0.max(rhs.0)
261        })
262    }
263    pub fn copysign(&self, rhs: Self) -> Self {
264        Self(libm::copysign(self.0, rhs.0))
265    }
266
267    pub fn from_bits(other: u64) -> Self {
268        Self(f64::from_bits(other))
269    }
270    pub fn is_nan(&self) -> bool {
271        self.0.is_nan()
272    }
273    pub fn is_infinity(&self) -> bool {
274        self.0.is_infinite()
275    }
276    pub fn is_negative_infinity(&self) -> bool {
277        self.0.is_infinite() && self.0 < 0.0
278    }
279
280    pub fn as_i32(&self) -> i32 {
281        self.0 as i32
282    }
283    pub fn as_u32(&self) -> u32 {
284        self.0 as u32
285    }
286    pub fn as_i64(&self) -> i64 {
287        self.0 as i64
288    }
289    pub fn as_u64(&self) -> u64 {
290        self.0 as u64
291    }
292    pub fn as_f32(&self) -> F32 {
293        F32(self.0 as f32)
294    }
295    pub fn reinterpret_as_i64(&self) -> i64 {
296        self.0.to_bits() as i64
297    }
298    pub fn to_bits(&self) -> u64 {
299        self.0.to_bits()
300    }
301}
302
303/// A value at runtime. This is essentially a duplicate of [ValType] just with additional values.
304///
305/// See <https://webassembly.github.io/spec/core/exec/runtime.html#values>
306// TODO implement missing variants
307#[derive(Clone, Copy, Debug, PartialEq)]
308pub enum Value {
309    I32(u32),
310    I64(u64),
311    F32(F32),
312    F64(F64),
313    V128([u8; 16]),
314    Ref(Ref),
315}
316
317#[derive(Clone, Copy, Debug, PartialEq, Eq)]
318pub enum Ref {
319    Null(RefType),
320    Func(FuncAddr),
321    Extern(ExternAddr),
322}
323
324impl Display for Ref {
325    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
326        match self {
327            Ref::Func(func_addr) => write!(f, "FuncRef({func_addr:?})"),
328            Ref::Extern(extern_addr) => write!(f, "ExternRef({extern_addr:?})"),
329            Ref::Null(ty) => write!(f, "Null({ty:?})"),
330        }
331    }
332}
333
334/// Represents the address of a function within a WebAssembly module.
335///
336/// Functions in WebAssembly modules can be either:
337/// - **Defined**: Declared and implemented within the module.
338/// - **Imported**: Declared in the module but implemented externally.
339///
340/// [`FuncAddr`] provides a unified representation for both types. Internally,
341/// the address corresponds to an index in a combined function namespace,
342/// typically represented as a vector.
343#[derive(Debug, Clone, Copy, PartialEq, Eq)]
344pub struct FuncAddr(pub usize);
345
346/// Represents the address of an external reference in the interpreter.
347///
348/// External references are managed at the interpreter level and are not part of
349/// the WebAssembly module itself. They are typically used to refer to host
350/// functions or objects that interact with the module.
351///
352/// Internally, [`ExternAddr`] corresponds to an index in a linear vector,
353/// enabling dynamic storage and retrieval of external values.
354#[derive(Debug, Clone, Copy, PartialEq, Eq)]
355pub struct ExternAddr(pub usize);
356
357#[derive(Clone, Copy, Debug, PartialEq)]
358pub enum RefValueTy {
359    Func,
360    Extern,
361}
362
363impl Value {
364    pub fn default_from_ty(ty: ValType) -> Self {
365        match ty {
366            ValType::NumType(NumType::I32) => Self::I32(0),
367            ValType::NumType(NumType::I64) => Self::I64(0),
368            ValType::NumType(NumType::F32) => Self::F32(F32(0.0)),
369            ValType::NumType(NumType::F64) => Self::F64(F64(0.0_f64)),
370            ValType::RefType(ref_type) => Self::Ref(Ref::Null(ref_type)),
371            ValType::VecType => Self::V128([0; 16]),
372        }
373    }
374
375    pub fn to_ty(&self) -> ValType {
376        match self {
377            Value::I32(_) => ValType::NumType(NumType::I32),
378            Value::I64(_) => ValType::NumType(NumType::I64),
379            Value::F32(_) => ValType::NumType(NumType::F32),
380            Value::F64(_) => ValType::NumType(NumType::F64),
381            Value::Ref(Ref::Null(ref_type)) => ValType::RefType(*ref_type),
382            Value::Ref(Ref::Func(_)) => ValType::RefType(RefType::FuncRef),
383            Value::Ref(Ref::Extern(_)) => ValType::RefType(RefType::ExternRef),
384            Value::V128(_) => ValType::VecType,
385        }
386    }
387}
388
389impl From<u32> for Value {
390    fn from(x: u32) -> Self {
391        Value::I32(x)
392    }
393}
394impl TryFrom<Value> for u32 {
395    type Error = ();
396
397    fn try_from(value: Value) -> Result<Self, Self::Error> {
398        match value {
399            Value::I32(x) => Ok(x),
400            _ => Err(()),
401        }
402    }
403}
404
405impl From<i32> for Value {
406    fn from(x: i32) -> Self {
407        Value::I32(x as u32)
408    }
409}
410impl TryFrom<Value> for i32 {
411    type Error = ();
412
413    fn try_from(value: Value) -> Result<Self, Self::Error> {
414        match value {
415            Value::I32(x) => Ok(x as i32),
416            _ => Err(()),
417        }
418    }
419}
420
421impl From<u64> for Value {
422    fn from(x: u64) -> Self {
423        Value::I64(x)
424    }
425}
426impl TryFrom<Value> for u64 {
427    type Error = ();
428
429    fn try_from(value: Value) -> Result<Self, Self::Error> {
430        match value {
431            Value::I64(x) => Ok(x),
432            _ => Err(()),
433        }
434    }
435}
436impl From<i64> for Value {
437    fn from(x: i64) -> Self {
438        Value::I64(x as u64)
439    }
440}
441impl TryFrom<Value> for i64 {
442    type Error = ();
443
444    fn try_from(value: Value) -> Result<Self, Self::Error> {
445        match value {
446            Value::I64(x) => Ok(x as i64),
447            _ => Err(()),
448        }
449    }
450}
451
452impl From<F32> for Value {
453    fn from(x: F32) -> Self {
454        Value::F32(x)
455    }
456}
457impl TryFrom<Value> for F32 {
458    type Error = ();
459
460    fn try_from(value: Value) -> Result<Self, Self::Error> {
461        match value {
462            Value::F32(x) => Ok(x),
463            _ => Err(()),
464        }
465    }
466}
467
468impl From<F64> for Value {
469    fn from(x: F64) -> Self {
470        Value::F64(x)
471    }
472}
473impl TryFrom<Value> for F64 {
474    type Error = ();
475
476    fn try_from(value: Value) -> Result<Self, Self::Error> {
477        match value {
478            Value::F64(x) => Ok(x),
479            _ => Err(()),
480        }
481    }
482}
483
484impl From<[u8; 16]> for Value {
485    fn from(value: [u8; 16]) -> Self {
486        Value::V128(value)
487    }
488}
489impl TryFrom<Value> for [u8; 16] {
490    type Error = ();
491
492    fn try_from(value: Value) -> Result<Self, Self::Error> {
493        match value {
494            Value::V128(x) => Ok(x),
495            _ => Err(()),
496        }
497    }
498}
499
500impl From<Ref> for Value {
501    fn from(value: Ref) -> Self {
502        Self::Ref(value)
503    }
504}
505
506impl TryFrom<Value> for Ref {
507    type Error = ();
508
509    fn try_from(value: Value) -> Result<Self, ()> {
510        match value {
511            Value::Ref(rref) => Ok(rref),
512            _ => Err(()),
513        }
514    }
515}
516
517#[cfg(test)]
518mod test {
519    use alloc::string::ToString;
520
521    use crate::{
522        value::{ExternAddr, F32, F64},
523        RefType,
524    };
525
526    use super::{FuncAddr, Ref};
527
528    #[test]
529    fn rounding_f32() {
530        let round_towards_0_f32 = F32(0.5 - f32::EPSILON).round();
531        let round_towards_1_f32 = F32(0.5 + f32::EPSILON).round();
532
533        assert_eq!(round_towards_0_f32, F32(0.0));
534        assert_eq!(round_towards_1_f32, F32(1.0));
535    }
536
537    #[test]
538    fn rounding_f64() {
539        let round_towards_0_f64 = F64(0.5 - f64::EPSILON).round();
540        let round_towards_1_f64 = F64(0.5 + f64::EPSILON).round();
541
542        assert_eq!(round_towards_0_f64, F64(0.0));
543        assert_eq!(round_towards_1_f64, F64(1.0));
544    }
545
546    #[test]
547    fn display_f32() {
548        for x in [
549            -1.0,
550            0.0,
551            1.0,
552            13.3,
553            f32::INFINITY,
554            f32::MAX,
555            f32::MIN,
556            f32::NAN,
557            f32::NEG_INFINITY,
558            core::f32::consts::PI,
559        ] {
560            let wrapped = F32(x).to_string();
561            let expected = x.to_string();
562            assert_eq!(wrapped, expected);
563        }
564    }
565
566    #[test]
567    fn display_f64() {
568        for x in [
569            -1.0,
570            0.0,
571            1.0,
572            13.3,
573            f64::INFINITY,
574            f64::MAX,
575            f64::MIN,
576            f64::NAN,
577            f64::NEG_INFINITY,
578            core::f64::consts::PI,
579        ] {
580            let wrapped = F64(x).to_string();
581            let expected = x.to_string();
582            assert_eq!(wrapped, expected);
583        }
584    }
585
586    #[test]
587    fn display_ref() {
588        assert_eq!(Ref::Func(FuncAddr(11)).to_string(), "FuncRef(FuncAddr(11))");
589        assert_eq!(
590            Ref::Extern(ExternAddr(13)).to_string(),
591            "ExternRef(ExternAddr(13))"
592        );
593        assert_eq!(Ref::Null(RefType::FuncRef).to_string(), "Null(FuncRef)");
594        assert_eq!(Ref::Null(RefType::ExternRef).to_string(), "Null(ExternRef)");
595    }
596}